/*
 * Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

/*
 * A DLL which is loaded by Java applications to handle communication
 * between Java VMs purposes of Accessbility.
 */

#include "AccessBridgeDebug.h"
#include "JavaAccessBridge.h"
#include "com_sun_java_accessibility_internal_AccessBridge.h" // programatically generated by JNI
#include "accessBridgeResource.h"
#include "accessBridgeCallbacks.h"
#include "AccessBridgeMessages.h"


#include <windows.h>
#include <stdio.h>

#include <jawt.h>
#include <jawt_md.h>

JavaAccessBridge *theJavaAccessBridge;
HWND theDialogWindow;

// re-entrance lock for receiving memory messages
CRITICAL_SECTION receiveMemoryIPCLock;


// unique broadcast msg. IDs gotten dymanically
extern UINT theFromJavaHelloMsgID;
extern UINT theFromWindowsHelloMsgID;


// ---------------------------------------------------------------------------

extern "C" {
    /**
     * DllMain - where Windows executables will load/unload us
     *
     */
    BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved) {

        switch (fdwReason) {
        case DLL_PROCESS_ATTACH:
            InitializeCriticalSection(&receiveMemoryIPCLock);
            theJavaAccessBridge = new JavaAccessBridge(hinstDll);
            break;

        case DLL_PROCESS_DETACH:        // A Windows executable unloaded us
            if (theJavaAccessBridge != (JavaAccessBridge *) 0) {
                delete theJavaAccessBridge;
                DeleteCriticalSection(&receiveMemoryIPCLock);
            }
            break;
        }
        return TRUE;
    }

    /**
     * Open a native window (and init the wrappers we'll be using)
     *
     */
    JNIEXPORT void JNICALL
    Java_com_sun_java_accessibility_internal_AccessBridge_runDLL(JNIEnv *env, jobject obj) {
        PrintDebugString("[INFO]: JavaAccessBridge.DLL runDLL() called");
        theJavaAccessBridge->javaRun(env, obj);
    }

    /**
     * Our window proc
     *
     */
    BOOL APIENTRY AccessBridgeDialogProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
        int command;
        COPYDATASTRUCT *sentToUs;
        char *package;

        switch (message) {
        case WM_INITDIALOG:
            PrintDebugString("[INFO]: In AccessBridgeDialog - Initializing");
            break;

        case WM_COMMAND:
            command = LOWORD (wParam);
            PrintDebugString("[INFO]: In AccessBridgeDialog - Got WM_COMMAND, command: %X", command);
            break;

            // call from Java with data for us to deliver
        case WM_COPYDATA:
            if (theDialogWindow == (HWND) wParam) {
                PrintDebugString("[INFO]: In AccessBridgeDialog - Got WM_COPYDATA from ourselves");
            } else {
                PrintDebugString("[INFO]: In AccessBridgeDialog - Got WM_COPYDATA from HWND %p", wParam);
                sentToUs = (COPYDATASTRUCT *) lParam;
                package = (char *) sentToUs->lpData;
                theJavaAccessBridge->processPackage(package, sentToUs->cbData);
            }
            break;

            // call from Java with data for us retrieve from memory mapped file and deliver
        case AB_MESSAGE_WAITING:
            // wParam == sourceHwnd
            // lParam == buffer size in shared memory
            if (theDialogWindow == (HWND) wParam) {
                PrintDebugString("[INFO]: In AccessBridgeDialog - Got AB_MESSAGE_WAITING from ourselves");
            } else {
                PrintDebugString("[INFO]: In AccessBridgeDialog - Got AB_MESSAGE_WAITING from HWND %p", wParam);
                LRESULT returnVal = theJavaAccessBridge->receiveMemoryPackage((HWND) wParam, (long) lParam);
            }
            break;

            // a JavaAccessBridge DLL is going away
        case AB_DLL_GOING_AWAY:
            PrintDebugString("[INFO]: In AccessBridgeDialog - Got AB_DLL_GOING_AWAY message");
            theJavaAccessBridge->WindowsATDestroyed((HWND) wParam);
            break;

        default:
            // the Windows AT is saying "hi"!
            // wParam == sourceHwnc; lParam unused
            if (message == theFromWindowsHelloMsgID) {
                // A new Windows AT just said "hi";
                // say "hi" back so it can mate up with us
                // otherwise don't do anything (e.g. don't set up data structures yet)
                PrintDebugString("[INFO]: In AccessBridgeDialog - Got theFromWindowsHelloMsgID message");
                theJavaAccessBridge->postHelloToWindowsDLLMsg((HWND) wParam);
            }
        }
        return FALSE;
    }

}


// -----------------------------


/**
 * Initialize the JavaAccessBridge
 *
 */
JavaAccessBridge::JavaAccessBridge(HINSTANCE hInstance) {
    windowsInstance = hInstance;
    ATs = (AccessBridgeATInstance *) 0;
    initializeFileLogger("java_access_bridge");
    initBroadcastMessageIDs();          // get the unique to us broadcast msg. IDs
}

extern DWORD JavaBridgeThreadId;

/**
 * Destroy the JavaAccessBridge
 *
 */
JavaAccessBridge::~JavaAccessBridge() {
    // inform all other AccessBridges that we're going away

    PrintDebugString("[INFO]: in JavaAccessBridge::~JavaAccessBridge()");

    // Send a shutdown message for those applications like StarOffice that do
    // send a shutdown message themselves.
    javaShutdown(NULL, 0);

    AccessBridgeATInstance *current = ATs;
    while (current != (AccessBridgeATInstance *) 0) {
        PrintDebugString("[INFO]:  telling %p we're going away", current->winAccessBridgeWindow);
                SendMessage(current->winAccessBridgeWindow,
                    AB_DLL_GOING_AWAY, (WPARAM) dialogWindow, (LPARAM) 0);
        current = current->nextATInstance;
    }

    PrintDebugString("[INFO]:  finished telling ATs about our demise");

        if(JavaBridgeThreadId)
                {
                PostThreadMessage(JavaBridgeThreadId,WM_USER+1,0,0);
                Sleep(100);
                }

    delete ATs;

    PrintDebugString("[INFO]:   finished deleting ATs");
    PrintDebugString("[INFO]: GOODBYE CRUEL WORLD...");
    finalizeFileLogger();
}


void
JavaAccessBridge::javaRun(JNIEnv *env, jobject obj) {
    MSG msg;

    PrintDebugString("[INFO]: JavaAccessBridge::javaRun(%p, %p) called", env, obj);

    if (env->GetJavaVM(&javaVM) != 0) {
        return; // huh!?!?!
    }
    PrintDebugString("[INFO]:   -> javaVM = %p", javaVM);

    if (javaVM->AttachCurrentThread((void **) &windowsThreadJNIEnv, NULL) != 0) {
        return; // huh!?!?!
    }
    PrintDebugString("[INFO]:  -> windowsThreadJNIEnv = %p", windowsThreadJNIEnv);

    javaThreadABObject = env->NewGlobalRef(obj);
    windowsThreadABObject = windowsThreadJNIEnv->NewGlobalRef(obj);

    // initialize the Java thread AccessBridge entry points
    javaThreadEntryPoints = new AccessBridgeJavaEntryPoints(env, javaThreadABObject);
    if (javaThreadEntryPoints->BuildJavaEntryPoints() == FALSE) {
        return;         // couldn't build our entry points; let's get out of here!
    }
    PrintDebugString("[INFO]:   all Java thread entry points successfully found.");

    // initialize the Windows thread AccessBridge entry points
    windowsThreadEntryPoints = new AccessBridgeJavaEntryPoints(windowsThreadJNIEnv,
                                                               windowsThreadABObject);
    if (windowsThreadEntryPoints->BuildJavaEntryPoints() == FALSE) {
        return;         // couldn't build our entry points; let's get out of here!
    }
    PrintDebugString("[INFO]:   all Windows thread entry points successfully found.");


    // open our window
    if (initWindow() == TRUE) {
        PrintDebugString("[INFO]:   Window created.  HWND = %p", dialogWindow);

        // post a broadcast msg.; let other AccessBridge DLLs know we exist
        postHelloToWindowsDLLMsg(HWND_BROADCAST);

        // do that message loop thing
        while (GetMessage(&msg, NULL, 0, 0)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    } else {
        PrintDebugString("[ERROR]:   FAILED TO CREATE WINDOW!!!");
    }

    javaVM->DetachCurrentThread();
}

/**
 * Bring up our window; make a connection to the rest of the world
 *
 */
BOOL
JavaAccessBridge::initWindow() {
    theDialogWindow = CreateDialog(windowsInstance,
                                   "ACCESSBRIDGESTATUSWINDOW", NULL,
                                   (DLGPROC) AccessBridgeDialogProc);

    // If window could not be created, return "failure".
    if (!theDialogWindow)
        return FALSE;

    dialogWindow = theDialogWindow;

    // Make the window visible, update its client area, & return "success".
    // DEBUG_CODE(ShowWindow (theDialogWindow, SW_SHOWNORMAL));
    // DEBUG_CODE(UpdateWindow (theDialogWindow));

    return TRUE;
}



// -----------------------


/**
 * postHelloToWindowsDLLMsg
 *          - PostMessage(theFromJavaHelloMsgID) to let one or
 *            all WindowDLLs we're here and have a vmID
 *
 *            destHwnd is either a single hwnd or HWND_BROADCAST
 *              (former if a reply, latter if we're just born)
 *            wParam is our HWND
 *            lParam is our vmID
 *
 */
void
JavaAccessBridge::postHelloToWindowsDLLMsg(HWND destHwnd) {
    PrintDebugString("[INFO]: In JavaAccessBridge::postHelloToWindowsDLLMsg");
    PrintDebugString("[INFO]:   calling PostMessage(%p, %X, %p, %p)",
                     destHwnd, theFromJavaHelloMsgID, dialogWindow, dialogWindow);
    PostMessage(destHwnd, theFromJavaHelloMsgID, (WPARAM) dialogWindow, (LPARAM) dialogWindow);
}


// -----------------------

/**
 * sendPackage - uses SendMessage(WM_COPYDATA) to do IPC messaging
 *                                with the Java AccessBridge DLL
 *
 */
void
JavaAccessBridge::sendPackage(char *buffer, int bufsize, HWND destHwnd) {
    COPYDATASTRUCT toCopy;
    toCopy.dwData = 0;          // 32-bits we could use for something...
    toCopy.cbData = bufsize;
    toCopy.lpData = buffer;

    SendMessage(destHwnd, WM_COPYDATA, (WPARAM) dialogWindow, (LPARAM) &toCopy);
}


/**
 * sendJavaEventPackage - walk through ATs, sending event messages to 'em
 *
 */
void
JavaAccessBridge::sendJavaEventPackage(char *buffer, int bufsize, long type) {

    PrintDebugString("[INFO]: JavaAccessBridge::sendJavaEventPackage(), type = %X", type);

    if (ATs == (AccessBridgeATInstance *) 0) {
        PrintDebugString("[ERROR]: ATs == 0! (shouldn't happen here!)");
    }

    AccessBridgeATInstance *ati = ATs;
    while (ati != (AccessBridgeATInstance *) 0) {
        ati->sendJavaEventPackage(buffer, bufsize, type);
        ati = ati->nextATInstance;
    }
}

/**
 * sendAccessibilityEventPackage - walk through ATs, sending event messages to 'em
 *
 */
void
JavaAccessBridge::sendAccessibilityEventPackage(char *buffer, int bufsize, long type) {

    PrintDebugString("[INFO]: JavaAccessBridge::sendAccessibilityEventPackage(), type = %X", type);

    if (ATs == (AccessBridgeATInstance *) 0) {
        PrintDebugString("[ERROR] ATs == 0! (shouldn't happen here!)");
    }

    AccessBridgeATInstance *ati = ATs;
    while (ati != (AccessBridgeATInstance *) 0) {
        ati->sendAccessibilityEventPackage(buffer, bufsize, type);
        ati = ati->nextATInstance;
    }
}




/**
 * receiveMemoryPackage - uses Memory-Mapped files to do IPC messaging
 *                        with the Java AccessBridge DLL, receiving the
 *                        message from Java AccessBridge DLL by reading the
 *                        contents of the shared memory mapped file that
 *                        is used for Java-initiated messages
 *
 */
BOOL
JavaAccessBridge::receiveMemoryPackage(HWND srcWindow, long bufsize) {
    char *IPCview;

    PrintDebugString("[INFO]: JavaAccessBridge::receiveMemoryPackage(%p, %d)", srcWindow, bufsize);

    // look-up the appropriate IPCview based on the srcHWND of the Windows AccessBridge DLL
    if (ATs == (AccessBridgeATInstance *) 0) {
        PrintDebugString("[ERROR]: - ATs == 0 (shouldn't happen in receiveMemoryPackage()!");
        return FALSE;
    }
    AccessBridgeATInstance *ati = ATs->findABATInstanceFromATHWND(srcWindow);
    if (ati != (AccessBridgeATInstance *) 0) {
        IPCview = (char *) ati->memoryMappedView;

        // wait for the lock if someone else has it (re-entrancy)
        EnterCriticalSection(&receiveMemoryIPCLock);
        {
            // set byte at end of buffer to indicate to caller that we have reached this point
            IPCview[bufsize] = 1;

            // process the package
            processPackage(IPCview, bufsize);
        }
        // release re-entrance lock
        LeaveCriticalSection(&receiveMemoryIPCLock);

        return TRUE;

    } else {
        //DEBUG_CODE(AppendToCallInfo("ERROR receiving memory package: couldn't find srcWindow"));
        PrintDebugString("[ERROR]: receiving memory package: couldn't find srcWindow");
        return FALSE;
    }
}

/**
 * processPackage - processes the output of SendMessage(WM_COPYDATA)
 *                                      to do IPC messaging with the Windows AccessBridge DLL
 *
 */
LRESULT
JavaAccessBridge::processPackage(char *buffer, int bufsize) {
    PrintDebugString("[INFO]: Processing package sent from Windows, bufsize = %d:", bufsize);

    PackageType *type = (PackageType *) buffer;
    LRESULT returnVal = 0;
    PrintDebugString("[INFO]:  PackageType = %X:", *type);
    jobject rAC;

    switch (*type) {


    case cMemoryMappedFileCreatedPackage:
        // Windows is telling us it created a memory mapped file for us to use
        // in repsonding to various information querying packages (see below)
        PrintDebugString("[INFO]:   type == cMemoryMappedFileCreatedPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(MemoryMappedFileCreatedPackage))) {
            MemoryMappedFileCreatedPackage *pkg =
                (MemoryMappedFileCreatedPackage *) (buffer + sizeof(PackageType));
            returnVal = MemoryMappedFileCreated((HWND)ABLongToHandle(pkg->bridgeWindow), pkg->filename);
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(MemoryMappedFileCreatedPackage));
        }
        break;

        // ------------ information querying packages ------------------

    case cReleaseJavaObjectPackage:
        PrintDebugString("[INFO]:   type == cReleaseJavaObjectPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(ReleaseJavaObjectPackage))) {
            ReleaseJavaObjectPackage *pkg =
                (ReleaseJavaObjectPackage *) (buffer + sizeof(PackageType));
            releaseJavaObject((jobject)pkg->object);
        } else {
            PrintDebugString("[ERROR]:   processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(ReleaseJavaObjectPackage));
        }
        break;

    case cGetAccessBridgeVersionPackage:
        PrintDebugString("[INFO]:   type == cGetAccessBridgeVersionPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessBridgeVersionPackage))) {
            GetAccessBridgeVersionPackage *pkg =
                (GetAccessBridgeVersionPackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->getVersionInfo(&(pkg->rVersionInfo));
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessBridgeVersionPackage));
        }
        break;

    case cIsJavaWindowPackage:
        PrintDebugString("[INFO]:   type == cIsJavaWindowPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(IsJavaWindowPackage))) {
            IsJavaWindowPackage *pkg =
                (IsJavaWindowPackage *) (buffer + sizeof(PackageType));
            pkg->rResult =
                windowsThreadEntryPoints->isJavaWindow(pkg->window);
            PrintDebugString("[INFO]:     -> returning result = %d", pkg->rResult);
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(IsJavaWindowPackage));
        }
        break;

    case cIsSameObjectPackage:
        PrintDebugString("[INFO]:   type == cIsSameObjectPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(IsSameObjectPackage))) {
            IsSameObjectPackage *pkg =
                (IsSameObjectPackage *) (buffer + sizeof(PackageType));
            pkg->rResult =
                windowsThreadEntryPoints->isSameObject((jobject)pkg->obj1, (jobject)pkg->obj2);
            PrintDebugString("[INFO]:     -> returning result = %d", pkg->rResult);
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(IsSameObjectPackage));
        }
        break;


    case cGetAccessibleContextFromHWNDPackage:
        PrintDebugString("[INFO]:    type == cGetAccessibleContextFromHWNDPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleContextFromHWNDPackage))) {
            GetAccessibleContextFromHWNDPackage *pkg =
                (GetAccessibleContextFromHWNDPackage *) (buffer + sizeof(PackageType));
            rAC = windowsThreadEntryPoints->getAccessibleContextFromHWND(pkg->window);
            pkg->rAccessibleContext = (JOBJECT64)rAC;
            pkg->rVMID = HandleToLong(dialogWindow);
            PrintDebugString("[INFO]:      -> returning AC = %p, vmID = %X", rAC, pkg->rVMID);
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleContextFromHWNDPackage));
        }
        break;


    case cGetHWNDFromAccessibleContextPackage:
        PrintDebugString("[INFO]:    type == cGetHWNDFromAccessibleContextPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetHWNDFromAccessibleContextPackage))) {
            GetHWNDFromAccessibleContextPackage *pkg =
                (GetHWNDFromAccessibleContextPackage *) (buffer + sizeof(PackageType));
            pkg->rHWND =
                ABHandleToLong( windowsThreadEntryPoints->getHWNDFromAccessibleContext((jobject)pkg->accessibleContext) );
            PrintDebugString("[INFO]:     -> returning HWND = %p", pkg->rHWND);
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetHWNDFromAccessibleContextPackage));
        }
        break;


        /* ===== utility methods ===== */

    case cSetTextContentsPackage:
        PrintDebugString("[INFO]:   type == cSetTextContentsPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(SetTextContentsPackage))) {
            SetTextContentsPackage *pkg =
                (SetTextContentsPackage *) (buffer + sizeof(PackageType));
            pkg->rResult =
                windowsThreadEntryPoints->setTextContents((jobject)pkg->accessibleContext, pkg->text);
            PrintDebugString("[INFO]:     -> returning result = %d", pkg->rResult);
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(SetTextContentsPackage));
        }
        break;

    case cGetParentWithRolePackage:
        if (bufsize == (sizeof(PackageType) + sizeof(GetParentWithRolePackage))) {
            GetParentWithRolePackage *pkg =
                (GetParentWithRolePackage *) (buffer + sizeof(PackageType));
            rAC = windowsThreadEntryPoints->getParentWithRole((jobject)pkg->accessibleContext, pkg->role);
            pkg->rAccessibleContext = (JOBJECT64)rAC;
            PrintDebugString("[INFO]:   type == cGetParentWithRolePackage\n"\
                             "     pkg->vmID: %X"\
                             "     pkg->accessibleContext: %p"\
                             "     pkg->role: %ls"\
                             "     -> returning rAccessibleContext = %p"\
                , pkg->vmID, (jobject)pkg->accessibleContext, pkg->role, rAC);
        } else {
            PrintDebugString("[ERROR]:   processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetParentWithRolePackage));
        }
        break;

    case cGetTopLevelObjectPackage:
        PrintDebugString("[INFO]:   type == cGetTopLevelObjectPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetTopLevelObjectPackage))) {
            GetTopLevelObjectPackage *pkg =
                (GetTopLevelObjectPackage *) (buffer + sizeof(PackageType));
            rAC = windowsThreadEntryPoints->getTopLevelObject((jobject)pkg->accessibleContext);
            pkg->rAccessibleContext = (JOBJECT64)rAC;
            PrintDebugString("[INFO]:      -> returning rAccessibleContext = %p", rAC);
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetTopLevelObjectPackage));
        }
        break;

    case cGetParentWithRoleElseRootPackage:
        PrintDebugString("[INFO]:   type == cGetParentWithRoleElseRootPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetParentWithRoleElseRootPackage))) {
            GetParentWithRoleElseRootPackage *pkg =
                (GetParentWithRoleElseRootPackage *) (buffer + sizeof(PackageType));
            rAC = windowsThreadEntryPoints->getParentWithRoleElseRoot((jobject)pkg->accessibleContext, pkg->role);
            pkg->rAccessibleContext = (JOBJECT64)rAC;
            PrintDebugString("[INFO]:      -> returning rAccessibleContext = %p", rAC);
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetParentWithRoleElseRootPackage));
        }
        break;

    case cGetObjectDepthPackage:
        PrintDebugString("[INFO]:   type == cGetObjectDepthPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetObjectDepthPackage))) {
            GetObjectDepthPackage *pkg =
                (GetObjectDepthPackage *) (buffer + sizeof(PackageType));
            pkg->rResult =
                windowsThreadEntryPoints->getObjectDepth((jobject)pkg->accessibleContext);
            PrintDebugString("[INFO]:     -> returning rResult = %d", pkg->rResult);
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetObjectDepthPackage));
        }
        break;

    case cGetActiveDescendentPackage:
        PrintDebugString("[INFO]:   type == cGetActiveDescendentPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetActiveDescendentPackage))) {
            GetActiveDescendentPackage *pkg =
                (GetActiveDescendentPackage *) (buffer + sizeof(PackageType));
            rAC = windowsThreadEntryPoints->getActiveDescendent((jobject)pkg->accessibleContext);
            pkg->rAccessibleContext = (JOBJECT64)rAC;
            PrintDebugString("[INFO]:  -> returning rAccessibleContext = %p", rAC);
        } else {
            PrintDebugString("[ERROR]:   processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetActiveDescendentPackage));
        }
        break;

    case cGetAccessibleContextAtPackage:
        PrintDebugString("[INFO]:   type == cGetAccessibleContextAtPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleContextAtPackage))) {
            GetAccessibleContextAtPackage *pkg =
                (GetAccessibleContextAtPackage *) (buffer + sizeof(PackageType));
            pkg->rAccessibleContext = (JOBJECT64)
                windowsThreadEntryPoints->getAccessibleContextAt(pkg->x, pkg->y,
                                                                 (jobject)pkg->AccessibleContext);
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleContextAtPackage));
        }
        break;

    case cGetAccessibleContextWithFocusPackage:
        PrintDebugString("[INFO]:   type == cGetAccessibleContextWithFocusPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleContextWithFocusPackage))) {
            GetAccessibleContextWithFocusPackage *pkg =
                (GetAccessibleContextWithFocusPackage *) (buffer + sizeof(PackageType));
            pkg->rAccessibleContext = (JOBJECT64)
                windowsThreadEntryPoints->getAccessibleContextWithFocus();
                        pkg->rVMID =  HandleToLong(dialogWindow);
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleContextWithFocusPackage));
        }
        break;

    case cGetAccessibleContextInfoPackage:
        PrintDebugString("[INFO]:   type == cGetAccessibleContextInfoPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleContextInfoPackage))) {
            GetAccessibleContextInfoPackage *pkg =
                (GetAccessibleContextInfoPackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->getAccessibleContextInfo(
                                                               (jobject)pkg->AccessibleContext, &(pkg->rAccessibleContextInfo));
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleContextInfoPackage));
        }
        break;

    case cGetAccessibleChildFromContextPackage:
        PrintDebugString("[INFO]:   type == cGetAccessibleChildFromContextPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleChildFromContextPackage))) {
            GetAccessibleChildFromContextPackage *pkg =
                (GetAccessibleChildFromContextPackage *) (buffer + sizeof(PackageType));
            pkg->rAccessibleContext = (JOBJECT64)windowsThreadEntryPoints->getAccessibleChildFromContext(
                                                                                              (jobject)pkg->AccessibleContext, pkg->childIndex);
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleChildFromContextPackage));
        }
        break;

    case cGetAccessibleParentFromContextPackage:
        PrintDebugString("[INFO]:    type == cGetAccessibleParentFromContextPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleParentFromContextPackage))) {
            GetAccessibleParentFromContextPackage *pkg =
                (GetAccessibleParentFromContextPackage *) (buffer + sizeof(PackageType));
            pkg->rAccessibleContext = (JOBJECT64)windowsThreadEntryPoints->getAccessibleParentFromContext(
                                                                                               (jobject)pkg->AccessibleContext);
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleParentFromContextPackage));
        }
        break;

        // ------------ begin AccessibleTable packages ------------------

    case cGetAccessibleTableInfoPackage:
        PrintDebugString("[INFO]:    ##### type == cGetAccessibleTableInfoPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleTableInfoPackage))) {
            GetAccessibleTableInfoPackage *pkg =
                (GetAccessibleTableInfoPackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->getAccessibleTableInfo((jobject)pkg->accessibleContext,
                                                             &(pkg->rTableInfo));
            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleTableInfoPackage));
        }
        break;

    case cGetAccessibleTableCellInfoPackage:
        PrintDebugString("[INFO]:    ##### type == cGetAccessibleTableCellInfoPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleTableCellInfoPackage))) {
            GetAccessibleTableCellInfoPackage *pkg =
                (GetAccessibleTableCellInfoPackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->getAccessibleTableCellInfo((jobject)pkg->accessibleTable, pkg->row,
                                                                 pkg->column, &(pkg->rTableCellInfo));
            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleTableCellInfoPackage));
        }
        break;

    case cGetAccessibleTableRowHeaderPackage:
        PrintDebugString("[INFO]:    ##### type == cGetAccessibleTableRowHeaderPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleTableRowHeaderPackage))) {
            GetAccessibleTableRowHeaderPackage *pkg =
                (GetAccessibleTableRowHeaderPackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->getAccessibleTableRowHeader((jobject)pkg->accessibleContext,
                                                                  &(pkg->rTableInfo));
            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleTableRowHeaderPackage));
        }
        break;

    case cGetAccessibleTableColumnHeaderPackage:
        PrintDebugString("[INFO]:    ##### type == cGetAccessibleTableColumnHeaderPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleTableColumnHeaderPackage))) {
            GetAccessibleTableColumnHeaderPackage *pkg =
                (GetAccessibleTableColumnHeaderPackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->getAccessibleTableColumnHeader((jobject)pkg->accessibleContext,
                                                                     &(pkg->rTableInfo));
            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleTableColumnHeaderPackage));
        }
        break;


    case cGetAccessibleTableRowDescriptionPackage:
        PrintDebugString("[INFO]:    ##### type == cGetAccessibleTableRowDescriptionPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleTableRowDescriptionPackage))) {
            GetAccessibleTableRowDescriptionPackage *pkg =
                (GetAccessibleTableRowDescriptionPackage *) (buffer + sizeof(PackageType));
            pkg->rAccessibleContext = (JOBJECT64)windowsThreadEntryPoints->getAccessibleTableRowDescription(
                                                                                                 (jobject)pkg->accessibleContext, pkg->row);
            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleTableRowDescriptionPackage));
        }
        break;

    case cGetAccessibleTableColumnDescriptionPackage:
        PrintDebugString("[INFO]:    ##### type == cGetAccessibleTableColumnDescriptionPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleTableColumnDescriptionPackage))) {
            GetAccessibleTableColumnDescriptionPackage *pkg =
                (GetAccessibleTableColumnDescriptionPackage *) (buffer + sizeof(PackageType));
            pkg->rAccessibleContext = (JOBJECT64)windowsThreadEntryPoints->getAccessibleTableColumnDescription(
                                                                                                    (jobject)pkg->accessibleContext, pkg->column);
            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleTableColumnDescriptionPackage));
        }
        break;

    case cGetAccessibleTableColumnSelectionCountPackage:
        PrintDebugString("[INFO]:    ##### type == cGetAccessibleTableColumnSelectionCountPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleTableColumnSelectionCountPackage))) {
            GetAccessibleTableColumnSelectionCountPackage *pkg =
                (GetAccessibleTableColumnSelectionCountPackage *) (buffer + sizeof(PackageType));
            pkg->rCount = windowsThreadEntryPoints->getAccessibleTableColumnSelectionCount(
                                                                                           (jobject)pkg->accessibleTable);
            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleTableColumnSelectionCountPackage));
        }
        break;

    case cGetAccessibleTableRowSelectionCountPackage:
        PrintDebugString("[INFO]:    ##### type == cGetAccessibleTableRowSelectionCountPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleTableRowSelectionCountPackage))) {
            GetAccessibleTableRowSelectionCountPackage *pkg =
                (GetAccessibleTableRowSelectionCountPackage *) (buffer + sizeof(PackageType));

            pkg->rCount = windowsThreadEntryPoints->getAccessibleTableRowSelectionCount(
                                                                                        (jobject)pkg->accessibleTable);

            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleTableRowSelectionCountPackage));
        }
        break;

    case cIsAccessibleTableRowSelectedPackage:
        PrintDebugString("[INFO]:    ##### type == cIsAccessibleTableRowSelectedPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(IsAccessibleTableRowSelectedPackage))) {
            IsAccessibleTableRowSelectedPackage *pkg =
                (IsAccessibleTableRowSelectedPackage *) (buffer + sizeof(PackageType));
            pkg->rResult = windowsThreadEntryPoints->isAccessibleTableRowSelected(
                                                                                  (jobject)pkg->accessibleTable, pkg->row);
            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(IsAccessibleTableRowSelectedPackage));
        }
        break;

    case cIsAccessibleTableColumnSelectedPackage:
        PrintDebugString("[INFO]:    ##### type == cIsAccessibleTableColumnSelectedPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(IsAccessibleTableColumnSelectedPackage))) {
            IsAccessibleTableColumnSelectedPackage *pkg =
                (IsAccessibleTableColumnSelectedPackage *) (buffer + sizeof(PackageType));
            pkg->rResult = windowsThreadEntryPoints->isAccessibleTableColumnSelected(
                                                                                     (jobject)pkg->accessibleTable, pkg->column);
            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(IsAccessibleTableColumnSelectedPackage));
        }
        break;

    case cGetAccessibleTableColumnSelectionsPackage:
        PrintDebugString("[INFO]:    ##### type == cGetAccessibleTableColumnSelectionsPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleTableColumnSelectionsPackage))) {
            GetAccessibleTableColumnSelectionsPackage *pkg =
                (GetAccessibleTableColumnSelectionsPackage *) (buffer + sizeof(PackageType));
            PrintDebugString("[INFO]:      ##### cGetAccessibleTableColumnSelectionsPackage count=%d", pkg->count);
            windowsThreadEntryPoints->getAccessibleTableColumnSelections(
                                                                         (jobject)pkg->accessibleTable, pkg->count, pkg->rSelections);

            for (int i = 0; i < pkg->count; i++) {
                PrintDebugString("[INFO]:      ##### cGetAccessibleTableColumnSelectionsPackage(%d)=%d", i, pkg->rSelections[i]);
            }

            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleTableColumnSelectionsPackage));
        }
        break;


    case cGetAccessibleTableRowSelectionsPackage:
        PrintDebugString("[INFO]:    ##### type == cGetAccessibleTableRowSelectionsPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleTableRowSelectionsPackage))) {
            GetAccessibleTableRowSelectionsPackage *pkg =
                (GetAccessibleTableRowSelectionsPackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->getAccessibleTableRowSelections(
                                                                      (jobject)pkg->accessibleTable, pkg->count, pkg->rSelections);
            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleTableRowSelectionsPackage));
        }
        break;

    case cGetAccessibleTableRowPackage:
        PrintDebugString("[INFO]:    ##### type == cGetAccessibleTableRowPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleTableRowPackage))) {
            GetAccessibleTableRowPackage *pkg =
                (GetAccessibleTableRowPackage *) (buffer + sizeof(PackageType));
            pkg->rRow = windowsThreadEntryPoints->getAccessibleTableRow(
                                                                        (jobject)pkg->accessibleTable, pkg->index);
            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleTableRowPackage));
        }
        break;

    case cGetAccessibleTableColumnPackage:
        PrintDebugString("[INFO]:    ##### type == cGetAccessibleTableColumnPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleTableColumnPackage))) {
            GetAccessibleTableColumnPackage *pkg =
                (GetAccessibleTableColumnPackage *) (buffer + sizeof(PackageType));
            pkg->rColumn = windowsThreadEntryPoints->getAccessibleTableColumn(
                                                                              (jobject)pkg->accessibleTable, pkg->index);
            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleTableColumnPackage));
        }
        break;

    case cGetAccessibleTableIndexPackage:
        PrintDebugString("[INFO]:    ##### type == cGetAccessibleTableIndexPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleTableIndexPackage))) {
            GetAccessibleTableIndexPackage *pkg =
                (GetAccessibleTableIndexPackage *) (buffer + sizeof(PackageType));
            pkg->rIndex = windowsThreadEntryPoints->getAccessibleTableIndex(
                                                                            (jobject)pkg->accessibleTable, pkg->row, pkg->column);
            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleTableIndexPackage));
        }
        break;

        // ------------ end AccessibleTable packages ------------------


        // ------------ begin AccessibleRelationSet packages ------------------

    case cGetAccessibleRelationSetPackage:
        PrintDebugString("[INFO]:    ##### type == cGetAccessibleRelationSetPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleRelationSetPackage))) {
            GetAccessibleRelationSetPackage *pkg =
                (GetAccessibleRelationSetPackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->getAccessibleRelationSet(
                                                               (jobject)pkg->accessibleContext, &(pkg->rAccessibleRelationSetInfo));
            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleRelationSetPackage));
        }
        break;

        // ------------ end AccessibleRelationSet packages ------------------

        // ------------ begin AccessibleHypertext packages ------------------

    case cGetAccessibleHypertextPackage:
        PrintDebugString("[INFO]:    ##### type == cGetAccessibleHypertextPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleHypertextPackage))) {
            GetAccessibleHypertextPackage *pkg =
                (GetAccessibleHypertextPackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->getAccessibleHypertext(
                                                             (jobject)pkg->accessibleContext, &(pkg->rAccessibleHypertextInfo));
            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleHypertextPackage));
        }
        break;

    case cActivateAccessibleHyperlinkPackage:
        PrintDebugString("[INFO]:    ##### type == cActivateAccessibleHyperlinkPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(ActivateAccessibleHyperlinkPackage))) {
            ActivateAccessibleHyperlinkPackage *pkg =
                (ActivateAccessibleHyperlinkPackage *) (buffer + sizeof(PackageType));
            pkg->rResult = windowsThreadEntryPoints->activateAccessibleHyperlink(
                                                                                 (jobject)pkg->accessibleContext, (jobject)pkg->accessibleHyperlink);
            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(ActivateAccessibleHyperlinkPackage));
        }
        break;

    case cGetAccessibleHyperlinkCountPackage:
        PrintDebugString("[INFO]:    ##### type == cGetAccessibleHyperlinkCountPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleHyperlinkCountPackage))) {
            GetAccessibleHyperlinkCountPackage *pkg =
                (GetAccessibleHyperlinkCountPackage *) (buffer + sizeof(PackageType));
            pkg->rLinkCount = windowsThreadEntryPoints->getAccessibleHyperlinkCount(
                                                                                    (jobject)pkg->accessibleContext);
            PrintDebugString("[INFO]:    ##### processing succeeded: pkg->rLinkCount = %d", pkg->rLinkCount);
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleHyperlinkCountPackage));
        }
        break;

    case cGetAccessibleHypertextExtPackage:
        PrintDebugString("[INFO]:    ##### type == cGetAccessibleHypertextExtPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleHypertextExtPackage))) {
            GetAccessibleHypertextExtPackage *pkg =
                (GetAccessibleHypertextExtPackage *) (buffer + sizeof(PackageType));
            pkg->rSuccess = windowsThreadEntryPoints->getAccessibleHypertextExt(
                                                                                (jobject)pkg->accessibleContext, pkg->startIndex, &(pkg->rAccessibleHypertextInfo));
            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleHypertextExtPackage));
        }
        break;

    case cGetAccessibleHypertextLinkIndexPackage:
        PrintDebugString("[INFO]:    ##### type == cGetAccessibleHypertextLinkIndexPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleHypertextLinkIndexPackage))) {
            GetAccessibleHypertextLinkIndexPackage *pkg =
                (GetAccessibleHypertextLinkIndexPackage *) (buffer + sizeof(PackageType));
            pkg->rLinkIndex = windowsThreadEntryPoints->getAccessibleHypertextLinkIndex(
                                                                                        (jobject)pkg->hypertext, pkg->charIndex);
            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleHypertextLinkIndexPackage));
        }
        break;

    case cGetAccessibleHyperlinkPackage:
        PrintDebugString("[INFO]:    ##### type == cGetAccessibleHyperlinkPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleHyperlinkPackage))) {
            GetAccessibleHyperlinkPackage *pkg =
                (GetAccessibleHyperlinkPackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->getAccessibleHyperlink((jobject)pkg->hypertext, pkg->linkIndex,
                                                             &(pkg->rAccessibleHyperlinkInfo));
            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleHyperlinkPackage));
        }
        break;

        // ------------ end AccessibleHypertext packages

        // ------------ begin Accessible KeyBindings, Icons and Actions

    case cGetAccessibleKeyBindingsPackage:
        PrintDebugString("[INFO]:    ##### type == cGetAccessibleKeyBindingsPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleKeyBindingsPackage))) {
            GetAccessibleKeyBindingsPackage *pkg =
                (GetAccessibleKeyBindingsPackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->getAccessibleKeyBindings (
                                                                (jobject)pkg->accessibleContext, &(pkg->rAccessibleKeyBindings));
            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleKeyBindingsPackage));
        }
        break;

    case cGetAccessibleIconsPackage:
        PrintDebugString("[INFO]:    ##### type == cGetAccessibleIconsPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleIconsPackage))) {
            GetAccessibleIconsPackage *pkg =
                (GetAccessibleIconsPackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->getAccessibleIcons (
                                                          (jobject)pkg->accessibleContext, &(pkg->rAccessibleIcons));
            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:   ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleIconsPackage));
        }
        break;


    case cGetAccessibleActionsPackage:
        PrintDebugString("[INFO]:    ##### type == cGetAccessibleActionsPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleActionsPackage))) {
            GetAccessibleActionsPackage *pkg =
                (GetAccessibleActionsPackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->getAccessibleActions (
                                                            (jobject)pkg->accessibleContext, &(pkg->rAccessibleActions));
            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleActionsPackage));
        }
        break;

    case cDoAccessibleActionsPackage:
        PrintDebugString("[INFO]:    ##### type == cDoAccessibleActionsPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(DoAccessibleActionsPackage))) {
            DoAccessibleActionsPackage *pkg =
                (DoAccessibleActionsPackage *) (buffer + sizeof(PackageType));
            pkg->rResult =
                windowsThreadEntryPoints->doAccessibleActions((jobject)pkg->accessibleContext, &(pkg->actionsToDo),
                                                              &(pkg->failure));
            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(DoAccessibleActionsPackage));
        }
        break;

        // ------------ begin addtional methods for Teton

    case cGetVirtualAccessibleNamePackage:
        PrintDebugString("[INFO]:    ##### type == GetVirtualAccessibleNamePackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetVirtualAccessibleNamePackage))) {
            GetVirtualAccessibleNamePackage *pkg =
                (GetVirtualAccessibleNamePackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->getVirtualAccessibleName ((const jobject)pkg->accessibleContext,
                                                             pkg->rName,
                                                             pkg->len);
            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetVirtualAccessibleNamePackage));
        }
        break;

    case cRequestFocusPackage:
        PrintDebugString("[INFO]:    ##### type == RequestFocusPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(RequestFocusPackage))) {
            RequestFocusPackage *pkg =
                (RequestFocusPackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->requestFocus (
                                                    (jobject)pkg->accessibleContext);
            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(RequestFocusPackage));
        }
        break;

    case cSelectTextRangePackage:
        PrintDebugString("[INFO]:    ##### type == SelectTextRangePackage");
        if (bufsize == (sizeof(PackageType) + sizeof(SelectTextRangePackage))) {
            SelectTextRangePackage *pkg =
                (SelectTextRangePackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->selectTextRange (
                                                       (jobject)pkg->accessibleContext, pkg->startIndex, pkg->endIndex);
            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(SelectTextRangePackage));
        }
        break;

    case cGetTextAttributesInRangePackage:
        PrintDebugString("[INFO]:    ##### type == GetTextAttributesInRangePackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetTextAttributesInRangePackage))) {
            GetTextAttributesInRangePackage *pkg =
                (GetTextAttributesInRangePackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->getTextAttributesInRange (
                                                                (jobject)pkg->accessibleContext, pkg->startIndex, pkg->endIndex,
                                                                (AccessibleTextAttributesInfo *)&(pkg->attributes),
                                                                &(pkg->rLength));
            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetTextAttributesInRangePackage));
        }
        break;


    case cGetVisibleChildrenCountPackage:
        PrintDebugString("[INFO]:    ##### type == GetVisibleChildrenCountPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetVisibleChildrenCountPackage))) {
            GetVisibleChildrenCountPackage *pkg =
                (GetVisibleChildrenCountPackage *) (buffer + sizeof(PackageType));
            pkg->rChildrenCount = windowsThreadEntryPoints->getVisibleChildrenCount ((jobject)pkg->accessibleContext);

            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetVisibleChildrenCountPackage));
        }
        break;

    case cGetVisibleChildrenPackage:
        PrintDebugString("[INFO]:    ##### type == GetVisibleChildrenPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetVisibleChildrenPackage))) {
            GetVisibleChildrenPackage *pkg =
                (GetVisibleChildrenPackage *) (buffer + sizeof(PackageType));
            pkg->rSuccess = windowsThreadEntryPoints->getVisibleChildren ((jobject)pkg->accessibleContext,
                                                                          pkg->startIndex,
                                                                          &(pkg->rVisibleChildrenInfo));

            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetVisibleChildrenPackage));
        }
        break;

    case cSetCaretPositionPackage:
        PrintDebugString("[INFO]:    ##### type == SetCaretPositionPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(SetCaretPositionPackage))) {
            SetCaretPositionPackage *pkg =
                (SetCaretPositionPackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->setCaretPosition (
                                                        (jobject)pkg->accessibleContext, pkg->position);
            PrintDebugString("[INFO]:    ##### processing succeeded");
        } else {
            PrintDebugString("[ERROR]:    ##### processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(SetCaretPositionPackage));
        }
        break;

        // ------------ end additional methods for Teton

        // ------------ end Accessible KeyBindings, Icons and Actions

        // ------------ Accessible Text packages ------------------

    case cGetAccessibleTextInfoPackage:
        PrintDebugString("[INFO]:    type == cGetAccessibleTextInfoPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleTextInfoPackage))) {
            GetAccessibleTextInfoPackage *pkg =
                (GetAccessibleTextInfoPackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->getAccessibleTextInfo((jobject)pkg->AccessibleContext,
                                                            &(pkg->rTextInfo), pkg->x, pkg->y);
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleTextInfoPackage));
        }
        break;

    case cGetAccessibleTextItemsPackage:
        PrintDebugString("[INFO]:    type == cGetAccessibleTextItemsPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleTextItemsPackage))) {
            GetAccessibleTextItemsPackage *pkg =
                (GetAccessibleTextItemsPackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->getAccessibleTextItems((jobject)pkg->AccessibleContext,
                                                             &(pkg->rTextItemsInfo), pkg->index);
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleTextInfoPackage));
        }
        break;

    case cGetAccessibleTextSelectionInfoPackage:
        PrintDebugString("[INFO]:    type == cGetAccessibleTextSelectionInfoPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleTextSelectionInfoPackage))) {
            GetAccessibleTextSelectionInfoPackage *pkg =
                (GetAccessibleTextSelectionInfoPackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->getAccessibleTextSelectionInfo(
                                                                     (jobject)pkg->AccessibleContext, &(pkg->rTextSelectionItemsInfo));
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleTextSelectionInfoPackage));
        }
        break;

    case cGetAccessibleTextAttributeInfoPackage:
        PrintDebugString("[INFO]:    type == cGetAccessibleTextAttributeInfoPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleTextAttributeInfoPackage))) {
            GetAccessibleTextAttributeInfoPackage *pkg =
                (GetAccessibleTextAttributeInfoPackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->getAccessibleTextAttributes(
                                                                  (jobject)pkg->AccessibleContext, pkg->index, (AccessibleTextAttributesInfo *) &(pkg->rAttributeInfo));
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleTextAttributeInfoPackage));
        }
        break;

    case cGetAccessibleTextRectInfoPackage:
        PrintDebugString("[INFO]:    type == cGetAccessibleTextRectInfoPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleTextRectInfoPackage))) {
            GetAccessibleTextRectInfoPackage *pkg =
                (GetAccessibleTextRectInfoPackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->getAccessibleTextRect((jobject)pkg->AccessibleContext,
                                                            &(pkg->rTextRectInfo), pkg->index);
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleTextRectInfoPackage));
        }
        break;

    case cGetCaretLocationPackage:
        PrintDebugString("[INFO]:    type == cGetCaretLocationPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetCaretLocationPackage))) {
            GetCaretLocationPackage *pkg =
                (GetCaretLocationPackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->getCaretLocation((jobject)pkg->AccessibleContext,
                                                            &(pkg->rTextRectInfo), pkg->index);
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetCaretLocationPackage));
        }
        break;

    case cGetAccessibleTextLineBoundsPackage:
        PrintDebugString("[INFO]:    type == cGetAccessibleTextLineBoundsPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleTextLineBoundsPackage))) {
            GetAccessibleTextLineBoundsPackage *pkg =
                (GetAccessibleTextLineBoundsPackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->getAccessibleTextLineBounds((jobject)pkg->AccessibleContext,
                                                                  pkg->index, &(pkg->rLineStart), &(pkg->rLineEnd));
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleTextLineBoundsPackage));
        }
        break;

    case cGetAccessibleTextRangePackage:
        PrintDebugString("[INFO]:    type == cGetAccessibleTextRangePackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleTextRangePackage))) {
            GetAccessibleTextRangePackage *pkg =
                (GetAccessibleTextRangePackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->getAccessibleTextRange((jobject)pkg->AccessibleContext,
                                                             pkg->start, pkg->end, (wchar_t *) &(pkg->rText), (sizeof(pkg->rText) / sizeof(wchar_t)));
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleTextRangePackage));
        }
        break;


        // ------------ Accessible Value packages ------------------

    case cGetCurrentAccessibleValueFromContextPackage:
        PrintDebugString("[INFO]:    type == cGetCurrentAccessibleValueFromContextPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetCurrentAccessibleValueFromContextPackage))) {
            GetCurrentAccessibleValueFromContextPackage *pkg =
                (GetCurrentAccessibleValueFromContextPackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->getCurrentAccessibleValueFromContext((jobject)pkg->AccessibleContext,
                                                                           (wchar_t *) &(pkg->rValue), (sizeof(pkg->rValue) / sizeof(wchar_t)));
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetCurrentAccessibleValueFromContextPackage));
        }
        break;

    case cGetMaximumAccessibleValueFromContextPackage:
        PrintDebugString("[INFO]:    type == cGetMaximumAccessibleValueFromContextPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetMaximumAccessibleValueFromContextPackage))) {
            GetMaximumAccessibleValueFromContextPackage *pkg =
                (GetMaximumAccessibleValueFromContextPackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->getMaximumAccessibleValueFromContext((jobject)pkg->AccessibleContext,
                                                                           (wchar_t *) &(pkg->rValue), (sizeof(pkg->rValue) / sizeof(wchar_t)));
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetMaximumAccessibleValueFromContextPackage));
        }
        break;

    case cGetMinimumAccessibleValueFromContextPackage:
        PrintDebugString("[INFO]:    type == cGetMinimumAccessibleValueFromContextPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetMinimumAccessibleValueFromContextPackage))) {
            GetMinimumAccessibleValueFromContextPackage *pkg =
                (GetMinimumAccessibleValueFromContextPackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->getMinimumAccessibleValueFromContext((jobject)pkg->AccessibleContext,
                                                                           (wchar_t *) &(pkg->rValue), (sizeof(pkg->rValue) / sizeof(wchar_t)));
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetMinimumAccessibleValueFromContextPackage));
        }
        break;

        // ------------ Accessible Selection packages ------------------

    case cAddAccessibleSelectionFromContextPackage:
        PrintDebugString("[INFO]:    type == cAddAccessibleSelectionFromContextPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(AddAccessibleSelectionFromContextPackage))) {
            AddAccessibleSelectionFromContextPackage *pkg =
                (AddAccessibleSelectionFromContextPackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->addAccessibleSelectionFromContext((jobject)pkg->AccessibleContext,
                                                                        pkg->index);
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(AddAccessibleSelectionFromContextPackage));
        }
        break;

    case cClearAccessibleSelectionFromContextPackage:
        PrintDebugString("[INFO]:    type == cClearAccessibleSelectionFromContextPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(ClearAccessibleSelectionFromContextPackage))) {
            ClearAccessibleSelectionFromContextPackage *pkg =
                (ClearAccessibleSelectionFromContextPackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->clearAccessibleSelectionFromContext((jobject)pkg->AccessibleContext);
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(ClearAccessibleSelectionFromContextPackage));
        }
        break;

    case cGetAccessibleSelectionFromContextPackage:
        PrintDebugString("[INFO]:    type == cGetAccessibleSelectionFromContextPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleSelectionFromContextPackage))) {
            GetAccessibleSelectionFromContextPackage *pkg =
                (GetAccessibleSelectionFromContextPackage *) (buffer + sizeof(PackageType));
            pkg->rAccessibleContext = (JOBJECT64)windowsThreadEntryPoints->getAccessibleSelectionFromContext(
                                                                                                  (jobject)pkg->AccessibleContext, pkg->index);
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleSelectionFromContextPackage));
        }
        break;

    case cGetAccessibleSelectionCountFromContextPackage:
        PrintDebugString("[INFO]:    type == cGetAccessibleSelectionCountFromContextPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(GetAccessibleSelectionCountFromContextPackage))) {
            GetAccessibleSelectionCountFromContextPackage *pkg =
                (GetAccessibleSelectionCountFromContextPackage *) (buffer + sizeof(PackageType));
            pkg->rCount = windowsThreadEntryPoints->getAccessibleSelectionCountFromContext(
                                                                                           (jobject)pkg->AccessibleContext);
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(GetAccessibleSelectionCountFromContextPackage));
        }
        break;

    case cIsAccessibleChildSelectedFromContextPackage:
        PrintDebugString("[INFO]:    type == cIsAccessibleChildSelectedFromContextPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(IsAccessibleChildSelectedFromContextPackage))) {
            IsAccessibleChildSelectedFromContextPackage *pkg =
                (IsAccessibleChildSelectedFromContextPackage *) (buffer + sizeof(PackageType));
            pkg->rResult = windowsThreadEntryPoints->isAccessibleChildSelectedFromContext(
                                                                                          (jobject)pkg->AccessibleContext, pkg->index);
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(IsAccessibleChildSelectedFromContextPackage));
        }
        break;

    case cRemoveAccessibleSelectionFromContextPackage:
        PrintDebugString("[INFO]:    type == cRemoveAccessibleSelectionFromContextPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(RemoveAccessibleSelectionFromContextPackage))) {
            RemoveAccessibleSelectionFromContextPackage *pkg =
                (RemoveAccessibleSelectionFromContextPackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->removeAccessibleSelectionFromContext((jobject)pkg->AccessibleContext,
                                                                           pkg->index);
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(RemoveAccessibleSelectionFromContextPackage));
        }
        break;

    case cSelectAllAccessibleSelectionFromContextPackage:
        PrintDebugString("[INFO]:    type == cSelectAllAccessibleSelectionFromContextPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(SelectAllAccessibleSelectionFromContextPackage))) {
            SelectAllAccessibleSelectionFromContextPackage *pkg =
                (SelectAllAccessibleSelectionFromContextPackage *) (buffer + sizeof(PackageType));
            windowsThreadEntryPoints->selectAllAccessibleSelectionFromContext((jobject)pkg->AccessibleContext);
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(SelectAllAccessibleSelectionFromContextPackage));
        }
        break;


        // ------------ event notification management packages ------------------

    case cAddJavaEventNotificationPackage:
        PrintDebugString("[INFO]:    type = cAddJavaEventNotificationPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(AddJavaEventNotificationPackage))) {
            AddJavaEventNotificationPackage *pkg =
                (AddJavaEventNotificationPackage *) (buffer + sizeof(PackageType));
            addJavaEventNotification(pkg->type, (HWND)ABLongToHandle( pkg->DLLwindow ) );
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(AddJavaEventNotificationPackage));
        }
        break;

    case cRemoveJavaEventNotificationPackage:
        PrintDebugString("[INFO]:    type = cRemoveJavaEventNotificationPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(RemoveJavaEventNotificationPackage))) {
            RemoveJavaEventNotificationPackage *pkg =
                (RemoveJavaEventNotificationPackage *) (buffer + sizeof(PackageType));
            removeJavaEventNotification(pkg->type, (HWND)ABLongToHandle( pkg->DLLwindow ));
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(RemoveJavaEventNotificationPackage));
        }
        break;

    case cAddAccessibilityEventNotificationPackage:
        PrintDebugString("[INFO]:    type = cAddAccessibilityEventNotificationPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(AddAccessibilityEventNotificationPackage))) {
            AddAccessibilityEventNotificationPackage *pkg =
                (AddAccessibilityEventNotificationPackage *) (buffer + sizeof(PackageType));
            addAccessibilityEventNotification(pkg->type, (HWND)ABLongToHandle(pkg->DLLwindow));
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(AddAccessibilityEventNotificationPackage));
        }
        break;

    case cRemoveAccessibilityEventNotificationPackage:
        PrintDebugString("[INFO]:    type = cRemoveAccessibilityEventNotificationPackage");
        if (bufsize == (sizeof(PackageType) + sizeof(RemoveAccessibilityEventNotificationPackage))) {
            RemoveAccessibilityEventNotificationPackage *pkg =
                (RemoveAccessibilityEventNotificationPackage *) (buffer + sizeof(PackageType));
            removeAccessibilityEventNotification(pkg->type, (HWND)ABLongToHandle(pkg->DLLwindow));
        } else {
            PrintDebugString("[ERROR]:    processing FAILED!! -> bufsize = %d; expectation = %d",
                             bufsize, sizeof(PackageType) + sizeof(RemoveAccessibilityEventNotificationPackage));
        }
        break;

    default:
        PrintDebugString("[ERROR]:    processing FAILED!! -> don't know how to handle type = %X", *type);
        returnVal = -1;
        break;
    }

    PrintDebugString("[INFO]:    package processing completed");
    return returnVal;
}


// -----------------------------


/**
 * MemoryMappedFileCreated
 *          - WindowsDLL letting us know it's created a memory-mapped file
 *            for IPC.  We need to open it and write a magic string into
 *            it to let the WindowsDLL know all is OK.  Also we need to
 *            set up our own data structures to communicate with the
 *            WindowsDLL
 *
 */
LRESULT
JavaAccessBridge::MemoryMappedFileCreated(HWND ATBridgeDLLWindow, char *filename) {
    PrintDebugString("[INFO]:   in MemoryMappedFileCreated(%p, %s)!", ATBridgeDLLWindow, filename);
    AccessBridgeATInstance *newAT =
        new AccessBridgeATInstance(dialogWindow, ATBridgeDLLWindow, filename, ATs);
    PrintDebugString("[INFO]:     just created a new ATInstance = %p, old = %p", newAT, ATs);
    ATs = newAT;

    LRESULT returnVal = ATs->initiateIPC();
    if (returnVal == 0) {
        PrintDebugString("[INFO]:   Successfully initiated IPC with AT!!!");
    } else {
        PrintDebugString("[ERROR]: Failed to initiate IPC with AT!!!");
    }

    return returnVal;
}


/**
 * WindowsATDestroyed - lets the JavaABDLL know a Windows AT disappeared
 *
 */
void
JavaAccessBridge::WindowsATDestroyed(HWND ATBridgeDLLWindow) {
    PrintDebugString("[INFO]: in JavaAccessBridge::WindowsATDestroyed(%p)", ATBridgeDLLWindow);
    if (ATs == (AccessBridgeATInstance *) 0) {
        PrintDebugString("[ERROR]: -> ATs == 0! (shouldn't happen here)");
        return;
    }

    AccessBridgeATInstance *currentAT = ATs;
    AccessBridgeATInstance *previousAT = ATs;
    if (ATs->winAccessBridgeWindow == ATBridgeDLLWindow) {
        ATs = ATs->nextATInstance;
        // remove event notification for this AT
        removeJavaEventNotification(currentAT->javaEventMask, ATBridgeDLLWindow);
        removeAccessibilityEventNotification(currentAT->accessibilityEventMask, ATBridgeDLLWindow);
        delete currentAT;
        PrintDebugString("[INFO]:   data structures successfully removed");
    } else {
        while (currentAT != (AccessBridgeATInstance *) NULL) {
            if (currentAT->winAccessBridgeWindow == ATBridgeDLLWindow) {
                previousAT->nextATInstance = currentAT->nextATInstance;
                delete currentAT;
                PrintDebugString("[INFO]:   data structures successfully removed");
                return;
            } else {
                previousAT = currentAT;
                currentAT = currentAT->nextATInstance;
            }
        }
        PrintDebugString("[ERROR]: couldn't find matching data structures!");
    }
}


// -----------------------------


/**
 * releaseJavaObject - lets the JavaVM know it can release the Java Object
 *
 * Note: once you have made this call, the JavaVM will garbage collect
 * the jobject you pass in.  If you later use that jobject in another
 * call, you will cause all maner of havoc!
 *
 */
void
JavaAccessBridge::releaseJavaObject(jobject object) {
    PrintDebugString("[INFO]: In JavaAccessBridge::releaseJavaObject");
    PrintDebugString("[INFO]:   object X: %p", object);
    if (windowsThreadJNIEnv != (JNIEnv *) 0) {
        windowsThreadJNIEnv->DeleteGlobalRef(object);
        PrintDebugString("[INFO]:   global reference deleted.", object);
    } else {
        PrintDebugString("[ERROR]: windowsThreadJNIEnv == 0");
    }
}

// -----------------------------

/**
 * addJavaEventNotification - this AT now wants this type of events
 *
 */
void
JavaAccessBridge::addJavaEventNotification(jlong type, HWND DLLwindow) {
    // walk through list of ATs, find this one and add this type
    // and, if we weren't listening for these before, ask Java for 'em
    PrintDebugString("[INFO]:   adding Java event type %016I64X to HWND %p", type, DLLwindow);
    AccessBridgeATInstance *ati = ATs;
    long globalEventMask = 0;
    while (ati != (AccessBridgeATInstance *) 0) {
        if (ati->winAccessBridgeWindow == DLLwindow) {
            ati->javaEventMask |= type;
            PrintDebugString("[INFO]:   found HWND, javaEventMask now is %X", ati->javaEventMask);
        } else {
            globalEventMask |= ati->javaEventMask;
        }
        ati = ati->nextATInstance;
    }
    PrintDebugString("[INFO]:   union of all Java AT event masks: %X", globalEventMask);
    if (!(globalEventMask & type)) {
        // no other ATs wanted this event;
        // start getting them from Java
        PrintDebugString("[INFO]:   no other AT wanted this Java event (so not registered); adding to AccessBridge.java");
        windowsThreadEntryPoints->addJavaEventNotification(type);
    }
}

/**
 * removeJavaEventNotification - this AT no longer wants this type of events
 *
 */
void
JavaAccessBridge::removeJavaEventNotification(jlong type, HWND DLLwindow) {
    // walk through list of ATs, find this one and remove this type
    // and, if no other AT wants 'em either, tell Java we no longer want 'em
    PrintDebugString("[INFO]:   removing Java event type %016I64X from HWND %p", type, DLLwindow);
    AccessBridgeATInstance *ati = ATs;
    long globalEventMask = 0;
    while (ati != (AccessBridgeATInstance *) 0) {
        if (ati->winAccessBridgeWindow == DLLwindow) {
            ati->javaEventMask &= (0xFFFFFFFF - type);
            PrintDebugString("[INFO]:   found HWND, javaEventMask now is %X", ati->javaEventMask);
        } else {
            globalEventMask |= ati->javaEventMask;
        }
        ati = ati->nextATInstance;
    }
    PrintDebugString("[INFO]:   union of all Java AT event masks: %X", globalEventMask);
    if (!(globalEventMask & type)) {
        // no other ATs wanted this event;
        // stop getting them from Java
        PrintDebugString("[INFO]:   no other AT wanted this Java event (so can remove); removing from AccessBridge.java");
        windowsThreadEntryPoints->removeJavaEventNotification(type);
    }
}


/**
 * addAccesibilityEventNotification - this AT now wants this type of events
 *
 */
void
JavaAccessBridge::addAccessibilityEventNotification(jlong type, HWND DLLwindow) {
    // walk through list of ATs, find this one and add this type
    // and, if we weren't listening for these before, ask Java for 'em
    PrintDebugString("[INFO]:   adding Accesibility event type %016I64X to HWND %p", type, DLLwindow);
    AccessBridgeATInstance *ati = ATs;
    long globalEventMask = 0;
    while (ati != (AccessBridgeATInstance *) 0) {
        if (ati->winAccessBridgeWindow == DLLwindow) {
            ati->accessibilityEventMask |= type;
            PrintDebugString("[INFO]:   found HWND, accessibilityEventMask now is %X", ati->accessibilityEventMask);
        } else {
            globalEventMask |= ati->accessibilityEventMask;
        }
        ati = ati->nextATInstance;
    }
    PrintDebugString("[INFO]:   union of all Accessibility AT event masks: %X", globalEventMask);
    if (!(globalEventMask & type)) {
        // no other ATs wanted this event;
        // start getting them from Java
        PrintDebugString("[INFO]:   no other AT wanted this Accesibility event (so not registered); adding to AccessBridge.java");
        windowsThreadEntryPoints->addAccessibilityEventNotification(type);
    }
}

/**
 * removeAccesibilityEventNotification - this AT no longer wants this type of events
 *
 */
void
JavaAccessBridge::removeAccessibilityEventNotification(jlong type, HWND DLLwindow) {
    // walk through list of ATs, find this one and remove this type
    // and, if no other AT wants 'em either, tell Java we no longer want 'em
    PrintDebugString("[INFO]:   removing Accesibility event type %016I64X from HWND %p", type, DLLwindow);
    AccessBridgeATInstance *ati = ATs;
    long globalEventMask = 0;
    while (ati != (AccessBridgeATInstance *) 0) {
        if (ati->winAccessBridgeWindow == DLLwindow) {
            ati->accessibilityEventMask &= (0xFFFFFFFF - type);
            PrintDebugString("[INFO]:   found HWND, accessibilityEventMask now is %X", ati->accessibilityEventMask);
        } else {
            globalEventMask |= ati->accessibilityEventMask;
        }
        ati = ati->nextATInstance;
    }
    PrintDebugString("[INFO]:   union of all Accessibility AT event masks: %X", globalEventMask);
    if (!(globalEventMask & type)) {
        // no other ATs wanted this event;
        // stop getting them from Java
        PrintDebugString("[INFO]:   no other AT wanted this Accessibility event (so can remove); removing from AccessBridge.java");
        windowsThreadEntryPoints->removeAccessibilityEventNotification(type);
    }
}




/**
 * firePropertyCaretChange
 *
 */
void
JavaAccessBridge::firePropertyCaretChange(JNIEnv *env, jobject callingObj,
                                          jobject event, jobject source,
                                          jint oldValue, jint newValue) {

    PrintDebugString("[INFO]: Java_com_sun_java_accessibility_internal_AccessBridge_propertyCaretChanged(%p, %p, %p, %p, %d, %d)",
                     env, callingObj, event,
                     source, oldValue, newValue);

    // sanity check
    if (ATs == (AccessBridgeATInstance *) 0) {
        PrintDebugString("[ERROR]: ATs == 0! (shouldn't happen here!)");
        return;         // panic!
    }

    // common setup
    char buffer[sizeof(PackageType) + sizeof(PropertyCaretChangePackage)];
    PackageType *type = (PackageType *) buffer;
    PropertyCaretChangePackage *pkg = (PropertyCaretChangePackage *) (buffer + sizeof(PackageType));
    *type = cPropertyCaretChangePackage;
    pkg->vmID = (long) dialogWindow;

    // make new Global Refs and send events only to those ATs that want 'em
    AccessBridgeATInstance *ati = ATs;
    while (ati != (AccessBridgeATInstance *) 0) {
        if (ati->accessibilityEventMask & cPropertyCaretChangeEvent) {

            PrintDebugString("[INFO]:   sending to AT");

            // make new GlobalRefs for this AT
            pkg->Event = (JOBJECT64)env->NewGlobalRef(event);
            pkg->AccessibleContextSource = (JOBJECT64)env->NewGlobalRef(source);
#ifdef ACCESSBRIDGE_ARCH_LEGACY // JOBJECT64 is jobject (32 bit pointer)
            PrintDebugString("[INFO]:   GlobalRef'd Event: %p"\
                             "          GlobalRef'd Source: %p", pkg->Event, pkg->AccessibleContextSource);
#else // JOBJECT64 is jlong (64 bit)
            PrintDebugString("[INFO]:   GlobalRef'd Event: %016I64X"\
                             "          GlobalRef'd Source: %016I64X", pkg->Event, pkg->AccessibleContextSource);
#endif

            pkg->oldPosition = oldValue;
            pkg->newPosition = newValue;

            ati->sendAccessibilityEventPackage(buffer, sizeof(buffer), cPropertyCaretChangeEvent);
        }
        ati = ati->nextATInstance;
    }
    PrintDebugString("[INFO]:  done with propertyCaretChange event");
}

/**
 * firePropertyDescriptionChange
 *
 */
void
JavaAccessBridge::firePropertyDescriptionChange(JNIEnv *env, jobject callingObj,
                                                jobject event, jobject source,
                                                jstring oldValue, jstring newValue){

    PrintDebugString("[INFO]: Java_com_sun_java_accessibility_internal_AccessBridge_propertyDescriptionChanged(%p, %p, %p, %p, %p, %p)",
                     env, callingObj, event,
                     source, oldValue, newValue);

    // sanity check
    if (ATs == (AccessBridgeATInstance *) 0) {
        PrintDebugString("[ERROR]: ATs == 0! (shouldn't happen here!)");
        return;         // panic!
    }

    // common setup
    const wchar_t *stringBytes;
    char buffer[sizeof(PackageType) + sizeof(PropertyDescriptionChangePackage)];
    PackageType *type = (PackageType *) buffer;
    PropertyDescriptionChangePackage *pkg = (PropertyDescriptionChangePackage *) (buffer + sizeof(PackageType));
    *type = cPropertyDescriptionChangePackage;
    pkg->vmID = (long) dialogWindow;

    // make new Global Refs and send events only to those ATs that want 'em
    AccessBridgeATInstance *ati = ATs;
    while (ati != (AccessBridgeATInstance *) 0) {
        if (ati->accessibilityEventMask & cPropertyCaretChangeEvent) {

            PrintDebugString("[INFO]:   sending to AT");

            // make new GlobalRefs for this AT
            pkg->Event = (JOBJECT64)env->NewGlobalRef(event);
            pkg->AccessibleContextSource = (JOBJECT64)env->NewGlobalRef(source);
#ifdef ACCESSBRIDGE_ARCH_LEGACY // JOBJECT64 is jobject (32 bit pointer)
            PrintDebugString("[INFO]:   GlobalRef'd Event: %p"\
                             "          GlobalRef'd Source: %p", pkg->Event, pkg->AccessibleContextSource);
#else // JOBJECT64 is jlong (64 bit)
            PrintDebugString("[INFO]:  GlobalRef'd Event: %016I64X"\
                             "  GlobalRef'd Source: %016I64X", pkg->Event, pkg->AccessibleContextSource);
#endif

            if (oldValue != (jstring) 0) {
                stringBytes = (const wchar_t *) env->GetStringChars(oldValue, 0);
                if (stringBytes == NULL) {
                    if (!env->ExceptionCheck()) {
                        jclass cls = env->FindClass("java/lang/OutOfMemoryError");
                        if (cls != NULL) {
                            env->ThrowNew(cls, NULL);
                        }
                    }
                    return;
                }
                wcsncpy(pkg->oldDescription, stringBytes, (sizeof(pkg->oldDescription) / sizeof(wchar_t)));
                env->ReleaseStringChars(oldValue, stringBytes);
            } else {
                wcsncpy(pkg->oldDescription, L"(null)", (sizeof(pkg->oldDescription) / sizeof(wchar_t)));
            }

            if (newValue != (jstring) 0) {
                stringBytes = (const wchar_t *) env->GetStringChars(newValue, 0);
                if (stringBytes == NULL) {
                   if (!env->ExceptionCheck()) {
                        jclass cls = env->FindClass("java/lang/OutOfMemoryError");
                        if (cls != NULL) {
                            env->ThrowNew(cls, NULL);
                        }
                    }
                    return;
                }
                wcsncpy(pkg->newDescription, stringBytes, (sizeof(pkg->newDescription) / sizeof(wchar_t)));
                env->ReleaseStringChars(newValue, stringBytes);
            } else {
                wcsncpy(pkg->newDescription, L"(null)", (sizeof(pkg->newDescription) / sizeof(wchar_t)));
            }

            ati->sendAccessibilityEventPackage(buffer, sizeof(buffer), cPropertyDescriptionChangeEvent);
        }
        ati = ati->nextATInstance;
    }
    PrintDebugString("[INFO]:   done with propertyDescriptionChange event");
}

/**
 * firePropertyNameChange
 *
 */
void
JavaAccessBridge::firePropertyNameChange(JNIEnv *env, jobject callingObj,
                                         jobject event, jobject source,
                                         jstring oldValue, jstring newValue){

    PrintDebugString("[INFO]: Java_com_sun_java_accessibility_internal_AccessBridge_propertyNameChanged(%p, %p, %p, %p, %p, %p)",
                     env, callingObj, event,
                     source, oldValue, newValue);

    // sanity check
    if (ATs == (AccessBridgeATInstance *) 0) {
        PrintDebugString("[ERROR]: ATs == 0! (shouldn't happen here!)");
        return;         // panic!
    }

    // common setup
    const wchar_t *stringBytes;
    char buffer[sizeof(PackageType) + sizeof(PropertyNameChangePackage)];
    PackageType *type = (PackageType *) buffer;
    PropertyNameChangePackage *pkg = (PropertyNameChangePackage *) (buffer + sizeof(PackageType));
    *type = cPropertyNameChangePackage;
    pkg->vmID = (long) dialogWindow;

    // make new Global Refs and send events only to those ATs that want 'em
    AccessBridgeATInstance *ati = ATs;
    while (ati != (AccessBridgeATInstance *) 0) {
        if (ati->accessibilityEventMask & cPropertyNameChangeEvent) {

            PrintDebugString("[INFO]:   sending to AT");

            // make new GlobalRefs for this AT
            pkg->Event = (JOBJECT64)env->NewGlobalRef(event);
            pkg->AccessibleContextSource = (JOBJECT64)env->NewGlobalRef(source);
#ifdef ACCESSBRIDGE_ARCH_LEGACY // JOBJECT64 is jobject (32 bit pointer)
            PrintDebugString("[INFO]:   GlobalRef'd Event: %p"\
                             "          GlobalRef'd Source: %p", pkg->Event, pkg->AccessibleContextSource);
#else // JOBJECT64 is jlong (64 bit)
            PrintDebugString("[INFO]:  GlobalRef'd Event: %016I64X"\
                             "         GlobalRef'd Source: %016I64X", pkg->Event, pkg->AccessibleContextSource);
#endif

            if (oldValue != (jstring) 0) {
                stringBytes = (const wchar_t *) env->GetStringChars(oldValue, 0);
                if (stringBytes == NULL) {
                    if (!env->ExceptionCheck()) {
                        jclass cls = env->FindClass("java/lang/OutOfMemoryError");
                        if (cls != NULL) {
                            env->ThrowNew(cls, NULL);
                        }
                    }
                    return;
                }
                wcsncpy(pkg->oldName, stringBytes, (sizeof(pkg->oldName) / sizeof(wchar_t)));
                env->ReleaseStringChars(oldValue, stringBytes);
            } else {
                wcsncpy(pkg->oldName, L"(null)", (sizeof(pkg->oldName) / sizeof(wchar_t)));
            }

            if (newValue != (jstring) 0) {
                stringBytes = (const wchar_t *) env->GetStringChars(newValue, 0);
                if (stringBytes == NULL) {
                    if (!env->ExceptionCheck()) {
                        jclass cls = env->FindClass("java/lang/OutOfMemoryError");
                        if (cls != NULL) {
                            env->ThrowNew(cls, NULL);
                        }
                    }
                    return;
                }
                wcsncpy(pkg->newName, stringBytes, (sizeof(pkg->newName) / sizeof(wchar_t)));
                env->ReleaseStringChars(newValue, stringBytes);
            } else {
                wcsncpy(pkg->newName, L"(null)", (sizeof(pkg->newName) / sizeof(wchar_t)));
            }

            ati->sendAccessibilityEventPackage(buffer, sizeof(buffer), cPropertyNameChangeEvent);
        }
        ati = ati->nextATInstance;
    }
    PrintDebugString("[INFO]:  done with propertyNameChange event");
}


/**
 * firePropertySelectionChange
 *
 */
void
JavaAccessBridge::firePropertySelectionChange(JNIEnv *env, jobject callingObj,
                                              jobject event, jobject source) {

    PrintDebugString("[INFO]: Java_com_sun_java_accessibility_internal_AccessBridge_propertySelectionChanged(%p, %p, %p, %p)",
                     env, callingObj, event, source);

    // sanity check
    if (ATs == (AccessBridgeATInstance *) 0) {
        PrintDebugString("[ERROR]: ATs == 0! (shouldn't happen here!)");
        return;         // panic!
    }

    // common setup
    char buffer[sizeof(PackageType) + sizeof(PropertySelectionChangePackage)];
    PackageType *type = (PackageType *) buffer;
    PropertySelectionChangePackage *pkg = (PropertySelectionChangePackage *) (buffer + sizeof(PackageType));
    *type = cPropertySelectionChangePackage;
    pkg->vmID = (long) dialogWindow;

    // make new Global Refs and send events only to those ATs that want 'em
    AccessBridgeATInstance *ati = ATs;
    while (ati != (AccessBridgeATInstance *) 0) {
        if (ati->accessibilityEventMask & cPropertySelectionChangeEvent) {

            PrintDebugString("[INFO]:   sending to AT");

            // make new GlobalRefs for this AT
            pkg->Event = (JOBJECT64)env->NewGlobalRef(event);
            pkg->AccessibleContextSource = (JOBJECT64)env->NewGlobalRef(source);
#ifdef ACCESSBRIDGE_ARCH_LEGACY // JOBJECT64 is jobject (32 bit pointer)
            PrintDebugString("[INFO]:   GlobalRef'd Event: %p"\
                             "          GlobalRef'd Source: %p", pkg->Event, pkg->AccessibleContextSource);
#else // JOBJECT64 is jlong (64 bit)
            PrintDebugString("[INFO]:   GlobalRef'd Event: %016I64X"\
                             "          GlobalRef'd Source: %016I64X", pkg->Event, pkg->AccessibleContextSource);
#endif

            ati->sendAccessibilityEventPackage(buffer, sizeof(buffer), cPropertySelectionChangeEvent);
        }
        ati = ati->nextATInstance;
    }
    PrintDebugString("[INFO]:   done with propertySelectionChange event");
}


/**
 * firePropertyStateChange
 *
 */
void
JavaAccessBridge::firePropertyStateChange(JNIEnv *env, jobject callingObj,
                                          jobject event, jobject source,
                                          jstring oldValue, jstring newValue){

    PrintDebugString("[INFO]: Java_com_sun_java_accessibility_internal_AccessBridge_propertyStateChanged(%p, %p, %p, %p, %p, %p)",
                     env, callingObj, event,
                     source, oldValue, newValue);

    // sanity check
    if (ATs == (AccessBridgeATInstance *) 0) {
        PrintDebugString("[ERROR]: ATs == 0! (shouldn't happen here!)");
        return;         // panic!
    }

    // common setup
    const wchar_t *stringBytes;
    char buffer[sizeof(PackageType) + sizeof(PropertyStateChangePackage)];
    PackageType *type = (PackageType *) buffer;
    PropertyStateChangePackage *pkg = (PropertyStateChangePackage *) (buffer + sizeof(PackageType));
    *type = cPropertyStateChangePackage;
    pkg->vmID = (long) dialogWindow;

    // make new Global Refs and send events only to those ATs that want 'em
    AccessBridgeATInstance *ati = ATs;
    while (ati != (AccessBridgeATInstance *) 0) {
        if (ati->accessibilityEventMask & cPropertyStateChangeEvent) {

            PrintDebugString("[INFO]:   sending to AT");

            // make new GlobalRefs for this AT
            pkg->Event = (JOBJECT64)env->NewGlobalRef(event);
            pkg->AccessibleContextSource = (JOBJECT64)env->NewGlobalRef(source);
#ifdef ACCESSBRIDGE_ARCH_LEGACY // JOBJECT64 is jobject (32 bit pointer)
            PrintDebugString("[INFO]:   GlobalRef'd Event: %p"\
                             "  GlobalRef'd Source: %p", pkg->Event, pkg->AccessibleContextSource);
#else // JOBJECT64 is jlong (64 bit)
            PrintDebugString("[INFO]:  GlobalRef'd Event: %016I64X"\
                             "  GlobalRef'd Source: %016I64X", pkg->Event, pkg->AccessibleContextSource);
#endif

            if (oldValue != (jstring) 0) {
                stringBytes = (const wchar_t *) env->GetStringChars(oldValue, 0);
                if (stringBytes == NULL) {
                    if (!env->ExceptionCheck()) {
                        jclass cls = env->FindClass("java/lang/OutOfMemoryError");
                        if (cls != NULL) {
                            env->ThrowNew(cls, NULL);
                        }
                    }
                    return;
                }
                wcsncpy(pkg->oldState, stringBytes, (sizeof(pkg->oldState) / sizeof(wchar_t)));
                env->ReleaseStringChars(oldValue, stringBytes);
            } else {
                wcsncpy(pkg->oldState, L"(null)", (sizeof(pkg->oldState) / sizeof(wchar_t)));
            }

            if (newValue != (jstring) 0) {
                stringBytes = (const wchar_t *) env->GetStringChars(newValue, 0);
                if (stringBytes == NULL) {
                    if (!env->ExceptionCheck()) {
                        jclass cls = env->FindClass("java/lang/OutOfMemoryError");
                        if (cls != NULL) {
                            env->ThrowNew(cls, NULL);
                        }
                    }
                    return;
                }
                wcsncpy(pkg->newState, stringBytes, (sizeof(pkg->newState) / sizeof(wchar_t)));
                env->ReleaseStringChars(newValue, stringBytes);
            } else {
                wcsncpy(pkg->newState, L"(null)", (sizeof(pkg->newState) / sizeof(wchar_t)));
            }

            ati->sendAccessibilityEventPackage(buffer, sizeof(buffer), cPropertyStateChangeEvent);
        }
        ati = ati->nextATInstance;
    }
    PrintDebugString("[INFO]:  done with propertyStateChange event");
}


/**
 * firePropertyTextChange
 *
 */
void
JavaAccessBridge::firePropertyTextChange(JNIEnv *env, jobject callingObj,
                                         jobject event, jobject source) {

    PrintDebugString("[INFO]: Java_com_sun_java_accessibility_internal_AccessBridge_propertyTextChanged(%p, %p, %p, %p)",
                     env, callingObj, event, source);

    // sanity check
    if (ATs == (AccessBridgeATInstance *) 0) {
        PrintDebugString("[ERROR]: ATs == 0! (shouldn't happen here!)");
        return;         // panic!
    }

    // common setup
    char buffer[sizeof(PackageType) + sizeof(PropertyTextChangePackage)];
    PackageType *type = (PackageType *) buffer;
    PropertyTextChangePackage *pkg = (PropertyTextChangePackage *) (buffer + sizeof(PackageType));
    *type = cPropertyTextChangePackage;
    pkg->vmID = (long) dialogWindow;

    // make new Global Refs and send events only to those ATs that want 'em
    AccessBridgeATInstance *ati = ATs;
    while (ati != (AccessBridgeATInstance *) 0) {
        if (ati->accessibilityEventMask & cPropertyTextChangeEvent) {

            PrintDebugString("[INFO]:   sending to AT");

            // make new GlobalRefs for this AT
            pkg->Event = (JOBJECT64)env->NewGlobalRef(event);
            pkg->AccessibleContextSource = (JOBJECT64)env->NewGlobalRef(source);
#ifdef ACCESSBRIDGE_ARCH_LEGACY // JOBJECT64 is jobject (32 bit pointer)
            PrintDebugString("[INFO]:   GlobalRef'd Event: %p"\
                             "          GlobalRef'd Source: %p",pkg->Event, pkg->AccessibleContextSource);
#else // JOBJECT64 is jlong (64 bit)
            PrintDebugString("[INFO]:  GlobalRef'd Event: %016I64X"\
                             "  GlobalRef'd Source: %016I64X", pkg->Event, pkg->AccessibleContextSource);
#endif

            ati->sendAccessibilityEventPackage(buffer, sizeof(buffer), cPropertyTextChangeEvent);
        }
        ati = ati->nextATInstance;
    }
    PrintDebugString("[INFO]:  done with propertyTextChange event");
}


/**
 * firePropertyValueChange
 *
 */
void
JavaAccessBridge::firePropertyValueChange(JNIEnv *env, jobject callingObj,
                                          jobject event, jobject source,
                                          jstring oldValue, jstring newValue){

    PrintDebugString("[INFO]: Java_com_sun_java_accessibility_internal_AccessBridge_propertyValueChanged(%p, %p, %p, %p, %p, %p)",
                     env, callingObj, event,
                     source, oldValue, newValue);

    // sanity check
    if (ATs == (AccessBridgeATInstance *) 0) {
        PrintDebugString("[ERROR]: ATs == 0! (shouldn't happen here!)");
        return;         // panic!
    }

    // common setup
    const wchar_t *stringBytes;
    char buffer[sizeof(PackageType) + sizeof(PropertyValueChangePackage)];
    PackageType *type = (PackageType *) buffer;
    PropertyValueChangePackage *pkg = (PropertyValueChangePackage *) (buffer + sizeof(PackageType));
    *type = cPropertyValueChangePackage;
    pkg->vmID = (long) dialogWindow;

    // make new Global Refs and send events only to those ATs that want 'em
    AccessBridgeATInstance *ati = ATs;
    while (ati != (AccessBridgeATInstance *) 0) {
        if (ati->accessibilityEventMask & cPropertyValueChangeEvent) {

            PrintDebugString("[INFO]:   sending to AT");

            // make new GlobalRefs for this AT
            pkg->Event = (JOBJECT64)env->NewGlobalRef(event);
            pkg->AccessibleContextSource = (JOBJECT64)env->NewGlobalRef(source);
#ifdef ACCESSBRIDGE_ARCH_LEGACY // JOBJECT64 is jobject (32 bit pointer)
            PrintDebugString("[INFO]:   GlobalRef'd Event: %p"\
                             "          GlobalRef'd Source: %p", pkg->Event, pkg->AccessibleContextSource);
#else // JOBJECT64 is jlong (64 bit)
            PrintDebugString("[INFO]:  GlobalRef'd Event: %016I64X"\
                             "  GlobalRef'd Source: %016I64X", pkg->Event, pkg->AccessibleContextSource);
#endif

            if (oldValue != (jstring) 0) {
                stringBytes = (const wchar_t *) env->GetStringChars(oldValue, 0);
                if (stringBytes == NULL) {
                    if (!env->ExceptionCheck()) {
                        jclass cls = env->FindClass("java/lang/OutOfMemoryError");
                        if (cls != NULL) {
                            env->ThrowNew(cls, NULL);
                        }
                    }
                    return;
                }
                wcsncpy(pkg->oldValue, stringBytes, (sizeof(pkg->oldValue) / sizeof(wchar_t)));
                env->ReleaseStringChars(oldValue, stringBytes);
            } else {
                wcsncpy(pkg->oldValue, L"(null)", (sizeof(pkg->oldValue) / sizeof(wchar_t)));
            }

            if (newValue != (jstring) 0) {
                stringBytes = (const wchar_t *) env->GetStringChars(newValue, 0);
                if (stringBytes == NULL) {
                    if (!env->ExceptionCheck()) {
                        jclass cls = env->FindClass("java/lang/OutOfMemoryError");
                        if (cls != NULL) {
                            env->ThrowNew(cls, NULL);
                        }
                    }
                    return;
                }
                wcsncpy(pkg->newValue, stringBytes, (sizeof(pkg->newValue) / sizeof(wchar_t)));
                env->ReleaseStringChars(newValue, stringBytes);
            } else {
                wcsncpy(pkg->newValue, L"(null)", (sizeof(pkg->newValue) / sizeof(wchar_t)));
            }

            ati->sendAccessibilityEventPackage(buffer, sizeof(buffer), cPropertyValueChangeEvent);
        }
        ati = ati->nextATInstance;
    }
    PrintDebugString("[INFO]:   done with propertyValueChange event");
}

/**
 * firePropertyVisibleDataChange
 *
 */
void
JavaAccessBridge::firePropertyVisibleDataChange(JNIEnv *env, jobject callingObj,
                                                jobject event, jobject source) {

    PrintDebugString("[INFO]: Java_com_sun_java_accessibility_internal_AccessBridge_propertyVisibleDataChanged(%p, %p, %p, %p)",
                     env, callingObj, event, source);

    // sanity check
    if (ATs == (AccessBridgeATInstance *) 0) {
        PrintDebugString("[ERROR]: ATs == 0! (shouldn't happen here!)");
        return;         // panic!
    }

    // common setup
    char buffer[sizeof(PackageType) + sizeof(PropertyVisibleDataChangePackage)];
    PackageType *type = (PackageType *) buffer;
    PropertyVisibleDataChangePackage *pkg = (PropertyVisibleDataChangePackage *) (buffer + sizeof(PackageType));
    *type = cPropertyVisibleDataChangePackage;
    pkg->vmID = (long) dialogWindow;

    // make new Global Refs and send events only to those ATs that want 'em
    AccessBridgeATInstance *ati = ATs;
    while (ati != (AccessBridgeATInstance *) 0) {
        if (ati->accessibilityEventMask & cPropertyVisibleDataChangeEvent) {

            PrintDebugString("[INFO]:   sending to AT");

            // make new GlobalRefs for this AT
            pkg->Event = (JOBJECT64)env->NewGlobalRef(event);
            pkg->AccessibleContextSource = (JOBJECT64)env->NewGlobalRef(source);
#ifdef ACCESSBRIDGE_ARCH_LEGACY // JOBJECT64 is jobject (32 bit pointer)
            PrintDebugString("[INFO]:   GlobalRef'd Event: %p"\
                             "          GlobalRef'd Source: %p", pkg->Event, pkg->AccessibleContextSource);
#else // JOBJECT64 is jlong (64 bit)
            PrintDebugString("[INFO]:  GlobalRef'd Event: %016I64X"\
                             "         GlobalRef'd Source: %016I64X", pkg->Event, pkg->AccessibleContextSource);
#endif

            ati->sendAccessibilityEventPackage(buffer, sizeof(buffer), cPropertyVisibleDataChangeEvent);
        }
        ati = ati->nextATInstance;
    }
    PrintDebugString("[INFO]:  done with propertyVisibleDataChange event");
}


/**
 * firePropertyChildChange
 *
 */
void
JavaAccessBridge::firePropertyChildChange(JNIEnv *env, jobject callingObj,
                                          jobject event, jobject source,
                                          jobject oldValue, jobject newValue){

    PrintDebugString("[INFO]: Java_com_sun_java_accessibility_internal_AccessBridge_propertyChildPropertyChanged(%p, %p, %p, %p, %p, %p)",
                     env, callingObj, event,
                     source, oldValue, newValue);

    // sanity check
    if (ATs == (AccessBridgeATInstance *) 0) {
        PrintDebugString("[ERROR]: ATs == 0! (shouldn't happen here!)");
        return;         // panic!
    }

    // common setup
    char buffer[sizeof(PackageType) + sizeof(PropertyChildChangePackage)];
    PackageType *type = (PackageType *) buffer;
    PropertyChildChangePackage *pkg = (PropertyChildChangePackage *) (buffer + sizeof(PackageType));
    *type = cPropertyChildChangePackage;
    pkg->vmID = (long) dialogWindow;

    // make new Global Refs and send events only to those ATs that want 'em
    AccessBridgeATInstance *ati = ATs;
    while (ati != (AccessBridgeATInstance *) 0) {
        if (ati->accessibilityEventMask & cPropertyChildChangeEvent) {

            PrintDebugString("[INFO]:  sending to AT");

            // make new GlobalRefs for this AT
            pkg->Event = (JOBJECT64)env->NewGlobalRef(event);
            pkg->AccessibleContextSource = (JOBJECT64)env->NewGlobalRef(source);
            pkg->oldChildAccessibleContext = (JOBJECT64)env->NewGlobalRef(oldValue);
            pkg->newChildAccessibleContext = (JOBJECT64)env->NewGlobalRef(newValue);
#ifdef ACCESSBRIDGE_ARCH_LEGACY // JOBJECT64 is jobject (32 bit pointer)
            PrintDebugString("[INFO]:  GlobalRef'd Event: %p"\
                             "  GlobalRef'd Source: %p"\
                             "  GlobalRef'd OldChildAC: %p"\
                             "  GlobalRef'd NewChildAC: %p"\
                            , pkg->Event, pkg->AccessibleContextSource, pkg->oldChildAccessibleContext, pkg->newChildAccessibleContext);
#else // JOBJECT64 is jlong (64 bit)
            PrintDebugString("[INFO]:   GlobalRef'd Event: %016I64X"\
                             "  GlobalRef'd Source: %016I64X"\
                             "  GlobalRef'd OldChildAC: %016I64X"\
                             "  GlobalRef'd NewChildAC: %016I64X"\
                             , pkg->Event, pkg->AccessibleContextSource, pkg->oldChildAccessibleContext, pkg->newChildAccessibleContext);
#endif

            ati->sendAccessibilityEventPackage(buffer, sizeof(buffer), cPropertyChildChangeEvent);
        }
        ati = ati->nextATInstance;
    }
    PrintDebugString("[INFO]:  done with propertyChildChange event");
}


/**
 * firePropertyActiveDescendentChange
 *
 */
void
JavaAccessBridge::firePropertyActiveDescendentChange(JNIEnv *env, jobject callingObj,
                                                     jobject event, jobject source,
                                                     jobject oldValue, jobject newValue){

    PrintDebugString("[INFO]: Java_com_sun_java_accessibility_internal_AccessBridge_propertyActiveDescendentPropertyChanged(%p, %p, %p, %p, %p, %p)",
                     env, callingObj, event,
                     source, oldValue, newValue);

    // sanity check
    if (ATs == (AccessBridgeATInstance *) 0) {
        PrintDebugString("[ERROR]: ATs == 0! (shouldn't happen here!)");
        return;         // panic!
    }

    // common setup
    char buffer[sizeof(PackageType) + sizeof(PropertyActiveDescendentChangePackage)];
    PackageType *type = (PackageType *) buffer;
    PropertyActiveDescendentChangePackage *pkg = (PropertyActiveDescendentChangePackage *) (buffer + sizeof(PackageType));
    *type = cPropertyActiveDescendentChangePackage;
    pkg->vmID = (long) dialogWindow;

    // make new Global Refs and send events only to those ATs that want 'em
    AccessBridgeATInstance *ati = ATs;
    while (ati != (AccessBridgeATInstance *) 0) {
        if (ati->accessibilityEventMask & cPropertyActiveDescendentChangeEvent) {

            PrintDebugString("[INFO]:   sending to AT");

            // make new GlobalRefs for this AT
            pkg->Event = (JOBJECT64)env->NewGlobalRef(event);
            pkg->AccessibleContextSource = (JOBJECT64)env->NewGlobalRef(source);
            pkg->oldActiveDescendentAccessibleContext = (JOBJECT64)env->NewGlobalRef(oldValue);
            pkg->newActiveDescendentAccessibleContext = (JOBJECT64)env->NewGlobalRef(newValue);
#ifdef ACCESSBRIDGE_ARCH_LEGACY // JOBJECT64 is jobject (32 bit pointer)
            PrintDebugString("[INFO]:   GlobalRef'd Event: %p"\
                             "  GlobalRef'd Source: %p"\
                             "  GlobalRef'd OldActiveDescendentAC: %p"\
                             "  GlobalRef'd NewActiveDescendentAC: %p"\
                             , pkg->Event, pkg->AccessibleContextSource, pkg->oldActiveDescendentAccessibleContext, pkg->newActiveDescendentAccessibleContext);
#else // JOBJECT64 is jlong (64 bit)
            PrintDebugString("[INFO]:  GlobalRef'd Event: %016I64X"\
                             "  GlobalRef'd Source: %016I64X"\
                             "  GlobalRef'd OldActiveDescendentAC: %016I64X"\
                             "  GlobalRef'd NewActiveDescendentAC: %016I64X"\
            , pkg->Event, pkg->AccessibleContextSource, pkg->oldActiveDescendentAccessibleContext, pkg->newActiveDescendentAccessibleContext);
#endif

            ati->sendAccessibilityEventPackage(buffer, sizeof(buffer), cPropertyActiveDescendentChangeEvent);
        }
        ati = ati->nextATInstance;
    }
    PrintDebugString("[INFO]:  done with propertyActiveChange event");
}

/**
 * firePropertyValueChange
 *
 */
void
JavaAccessBridge::firePropertyTableModelChange(JNIEnv *env, jobject callingObj,
                                               jobject event, jobject source,
                                               jstring oldValue, jstring newValue){

    PrintDebugString("[INFO]: Java_com_sun_java_accessibility_internal_AccessBridge_propertyTableModelChange(%p, %p, %p, %p, %p, %p)",
                     env, callingObj, event,
                     source, oldValue, newValue);

    // sanity check
    if (ATs == (AccessBridgeATInstance *) 0) {
        PrintDebugString("[ERROR]: ATs == 0! (shouldn't happen here!)");
        return;         // panic!
    }

    // common setup
    const wchar_t *stringBytes;
    char buffer[sizeof(PackageType) + sizeof(PropertyTableModelChangePackage)];
    PackageType *type = (PackageType *) buffer;
    PropertyTableModelChangePackage *pkg = (PropertyTableModelChangePackage *) (buffer + sizeof(PackageType));
    *type = cPropertyTableModelChangePackage;
    pkg->vmID = (long) dialogWindow;

    // make new Global Refs and send events only to those ATs that want 'em
    AccessBridgeATInstance *ati = ATs;
    while (ati != (AccessBridgeATInstance *) 0) {
        if (ati->accessibilityEventMask & cPropertyTableModelChangeEvent) {

            PrintDebugString("  sending to AT");

            // make new GlobalRefs for this AT
            pkg->Event = (JOBJECT64)env->NewGlobalRef(event);
            pkg->AccessibleContextSource = (JOBJECT64)env->NewGlobalRef(source);
#ifdef ACCESSBRIDGE_ARCH_LEGACY // JOBJECT64 is jobject (32 bit pointer)
            PrintDebugString("[INFO]:   GlobalRef'd Event: %p"\
                             "          GlobalRef'd Source: %p", pkg->Event, pkg->AccessibleContextSource);
#else // JOBJECT64 is jlong (64 bit)
            PrintDebugString("[INFO]:   GlobalRef'd Event: %016I64X"\
                             "          GlobalRef'd Source: %016I64X", pkg->Event, pkg->AccessibleContextSource);
#endif

            if (oldValue != (jstring) 0) {
                stringBytes = (const wchar_t *) env->GetStringChars(oldValue, 0);
                if (stringBytes == NULL) {
                    if (!env->ExceptionCheck()) {
                        jclass cls = env->FindClass("java/lang/OutOfMemoryError");
                        if (cls != NULL) {
                            env->ThrowNew(cls, NULL);
                        }
                    }
                    return;
                }
                wcsncpy(pkg->oldValue, stringBytes, (sizeof(pkg->oldValue) / sizeof(wchar_t)));
                env->ReleaseStringChars(oldValue, stringBytes);
            } else {
                wcsncpy(pkg->oldValue, L"(null)", (sizeof(pkg->oldValue) / sizeof(wchar_t)));
            }

            if (newValue != (jstring) 0) {
                stringBytes = (const wchar_t *) env->GetStringChars(newValue, 0);
                if (stringBytes == NULL) {
                    if (!env->ExceptionCheck()) {
                        jclass cls = env->FindClass("java/lang/OutOfMemoryError");
                        if (cls != NULL) {
                            env->ThrowNew(cls, NULL);
                        }
                    }
                    return;
                }
                wcsncpy(pkg->newValue, stringBytes, (sizeof(pkg->newValue) / sizeof(wchar_t)));
                env->ReleaseStringChars(newValue, stringBytes);
            } else {
                wcsncpy(pkg->newValue, L"(null)", (sizeof(pkg->newValue) / sizeof(wchar_t)));
            }

            ati->sendAccessibilityEventPackage(buffer, sizeof(buffer), cPropertyTableModelChangeEvent);
        }
        ati = ati->nextATInstance;
    }
    PrintDebugString("[INFO]:  done with propertyTableModelChange event");
}



#ifdef ACCESSBRIDGE_ARCH_LEGACY // JOBJECT64 is jobject (32 bit pointer)
#define PRINT_GLOBALREFS() \
    PrintDebugString("[INFO]:   GlobalRef'd Event: %p"\
                     "          GlobalRef'd Source: %p", pkg->Event, pkg->AccessibleContextSource);
#else // JOBJECT64 is jlong (64 bit)
#define PRINT_GLOBALREFS() \
    PrintDebugString("[INFO]:  GlobalRef'd Event: %016I64X"\
                     "  GlobalRef'd Source: %016I64X", pkg->Event, pkg->AccessibleContextSource);
#endif

#define FIRE_EVENT(function, packageStruct, packageConstant, eventConstant)             \
    void JavaAccessBridge::function(JNIEnv *env, jobject callingObj,                    \
                                    jobject eventObj, jobject source) {                 \
                                                                                        \
        PrintDebugString("[INFO]: Firing event id = %d(%p, %p, %p, %p); vmID = %X",     \
                        eventConstant, env, callingObj, eventObj, source, dialogWindow);\
                                                                                        \
        /* sanity check */                                                              \
        if (ATs == (AccessBridgeATInstance *) 0) {                                      \
            PrintDebugString("[ERROR]: ATs == 0! (shouldn't happen here!)");            \
            return;         /* panic! */                                                \
        }                                                                               \
                                                                                        \
        /* common setup */                                                              \
        char buffer[sizeof(PackageType) + sizeof(packageStruct)];                       \
        PackageType *type = (PackageType *) buffer;                                     \
        packageStruct *pkg = (packageStruct *) (buffer + sizeof(PackageType));          \
        *type = packageConstant;                                                        \
        pkg->vmID = (long) dialogWindow;                                                \
                                                                                        \
        /* make new Global Refs, send events only to those ATs that want 'em */         \
        AccessBridgeATInstance *ati = ATs;                                              \
        while (ati != (AccessBridgeATInstance *) 0) {                                   \
            PrintDebugString("[INFO]: javaEventMask = %X eventConstant=%d pkg->vmID=%X",\
                             ati->javaEventMask, eventConstant, pkg->vmID );            \
            if (ati->javaEventMask & eventConstant) {                                   \
                                                                                        \
                PrintDebugString("[INFO]:   sending to AT");                            \
                /* make new GlobalRefs for this AT */                                   \
                pkg->Event = (JOBJECT64)env->NewGlobalRef(eventObj);                    \
                pkg->AccessibleContextSource = (JOBJECT64)env->NewGlobalRef(source);    \
                PRINT_GLOBALREFS()                                                      \
                                                                                        \
                ati->sendJavaEventPackage(buffer, sizeof(buffer), eventConstant);       \
            }                                                                           \
            ati = ati->nextATInstance;                                                  \
        }                                                                               \
        PrintDebugString("[INFO]:   done with firing AWT event");                       \
    }

    void JavaAccessBridge::javaShutdown(JNIEnv *env, jobject callingObj) {

        PrintDebugString("[INFO]: Firing event id = %d(%p, %p); vmID = %X",
                         cJavaShutdownEvent, env, callingObj, dialogWindow);

        /* sanity check */
        if (ATs == (AccessBridgeATInstance *) 0) {
            PrintDebugString("[ERROR]: ATs == 0! (shouldn't happen here!)");
            return;             /* panic! */
        }

        /* common setup */
        char buffer[sizeof(PackageType) + sizeof(JavaShutdownPackage)];
        PackageType *type = (PackageType *) buffer;
        JavaShutdownPackage *pkg = (JavaShutdownPackage *) (buffer + sizeof(PackageType));
        *type = cJavaShutdownPackage;
        pkg->vmID = (long) dialogWindow;

        /* make new Global Refs, send events only to those ATs that want 'em */
        AccessBridgeATInstance *ati = ATs;
        while (ati != (AccessBridgeATInstance *) 0) {
            if (ati->javaEventMask & cJavaShutdownEvent) {
                PrintDebugString("[INFO]:   sending to AT");
                ati->sendJavaEventPackage(buffer, sizeof(buffer), cJavaShutdownEvent);
            }
            ati = ati->nextATInstance;
        }
        PrintDebugString("[INFO]:   done with firing AWT event");
    }

    FIRE_EVENT(fireFocusGained, FocusGainedPackage, cFocusGainedPackage, cFocusGainedEvent)
    FIRE_EVENT(fireFocusLost, FocusLostPackage, cFocusLostPackage, cFocusLostEvent)
    FIRE_EVENT(fireCaretUpdate, CaretUpdatePackage, cCaretUpdatePackage, cCaretUpdateEvent)
    FIRE_EVENT(fireMouseClicked, MouseClickedPackage, cMouseClickedPackage, cMouseClickedEvent)
    FIRE_EVENT(fireMouseEntered, MouseEnteredPackage, cMouseEnteredPackage, cMouseEnteredEvent)
    FIRE_EVENT(fireMouseExited, MouseExitedPackage, cMouseExitedPackage, cMouseExitedEvent)
    FIRE_EVENT(fireMousePressed, MousePressedPackage, cMousePressedPackage, cMousePressedEvent)
    FIRE_EVENT(fireMouseReleased, MouseReleasedPackage, cMouseReleasedPackage, cMouseReleasedEvent)
    FIRE_EVENT(fireMenuCanceled, MenuCanceledPackage, cMenuCanceledPackage, cMenuCanceledEvent)
    FIRE_EVENT(fireMenuDeselected, MenuDeselectedPackage, cMenuDeselectedPackage, cMenuDeselectedEvent)
    FIRE_EVENT(fireMenuSelected, MenuSelectedPackage, cMenuSelectedPackage, cMenuSelectedEvent)
    FIRE_EVENT(firePopupMenuCanceled, PopupMenuCanceledPackage, cPopupMenuCanceledPackage, cPopupMenuCanceledEvent)
    FIRE_EVENT(firePopupMenuWillBecomeInvisible, PopupMenuWillBecomeInvisiblePackage, cPopupMenuWillBecomeInvisiblePackage, cPopupMenuWillBecomeInvisibleEvent)
    FIRE_EVENT(firePopupMenuWillBecomeVisible, PopupMenuWillBecomeVisiblePackage, cPopupMenuWillBecomeVisiblePackage, cPopupMenuWillBecomeVisibleEvent)


    // -----------------------------


extern "C" {        // event stuff from AccessBridge.h, generated by JNI

    JNIEXPORT void JNICALL
    Java_com_sun_java_accessibility_internal_AccessBridge_sendDebugString(JNIEnv *env, jobject callingObj, jstring debugStr) {

        const wchar_t *stringBytes;
        stringBytes = (const wchar_t *) env->GetStringChars(debugStr, 0);
        if (stringBytes == NULL) {
            if (!env->ExceptionCheck()) {
                jclass cls = env->FindClass("java/lang/OutOfMemoryError");
                if (cls != NULL) {
                    env->ThrowNew(cls, NULL);
                }
            }
            return;
        }
        wPrintJavaDebugString(L"AccessBridge.java: %ls", stringBytes);
        env->ReleaseStringChars(debugStr, stringBytes);
    }

    JNIEXPORT void JNICALL
    Java_com_sun_java_accessibility_internal_AccessBridge_propertyCaretChange(JNIEnv *env, jobject callingObj,
                                                                        jobject event, jobject source,
                                                                        jint oldValue, jint newValue) {
        theJavaAccessBridge->firePropertyCaretChange(env, callingObj,
                                                        event, source,
                                                        oldValue, newValue);
    }

    JNIEXPORT void JNICALL
    Java_com_sun_java_accessibility_internal_AccessBridge_propertyDescriptionChange(JNIEnv *env, jobject callingObj,
                                                                            jobject event, jobject source,
                                                                            jstring oldValue, jstring newValue) {
        theJavaAccessBridge->firePropertyDescriptionChange(env, callingObj,
                                                            event, source,
                                                            oldValue, newValue);
    }

    JNIEXPORT void JNICALL
    Java_com_sun_java_accessibility_internal_AccessBridge_propertyNameChange(JNIEnv *env, jobject callingObj,
                                                                    jobject event, jobject source,
                                                                    jstring oldValue, jstring newValue) {
        theJavaAccessBridge->firePropertyNameChange(env, callingObj,
                                                    event, source,
                                                    oldValue, newValue);
    }

    JNIEXPORT void JNICALL
    Java_com_sun_java_accessibility_internal_AccessBridge_propertySelectionChange(JNIEnv *env, jobject callingObj,
                                                                            jobject event, jobject source) {
        theJavaAccessBridge->firePropertySelectionChange(env, callingObj,
                                                            event, source);
    }

    JNIEXPORT void JNICALL
    Java_com_sun_java_accessibility_internal_AccessBridge_propertyStateChange(JNIEnv *env, jobject callingObj,
                                                                        jobject event, jobject source,
                                                                        jstring oldValue, jstring newValue) {
        theJavaAccessBridge->firePropertyStateChange(env, callingObj,
                                                        event, source,
                                                        oldValue, newValue);
    }

    JNIEXPORT void JNICALL
    Java_com_sun_java_accessibility_internal_AccessBridge_propertyTextChange(JNIEnv *env, jobject callingObj,
                                                                    jobject event,  jobject source) {
        theJavaAccessBridge->firePropertyTextChange(env, callingObj,
                                                    event, source);
    }

    JNIEXPORT void JNICALL
    Java_com_sun_java_accessibility_internal_AccessBridge_propertyValueChange(JNIEnv *env, jobject callingObj,
                                                                        jobject event, jobject source,
                                                                        jstring oldValue, jstring newValue) {
        theJavaAccessBridge->firePropertyValueChange(env, callingObj,
                                                        event, source,
                                                        oldValue, newValue);
    }

    JNIEXPORT void JNICALL
    Java_com_sun_java_accessibility_internal_AccessBridge_propertyVisibleDataChange(JNIEnv *env, jobject callingObj,
                                                                            jobject event,  jobject source) {
        theJavaAccessBridge->firePropertyVisibleDataChange(env, callingObj,
                                                            event, source);
    }

    JNIEXPORT void JNICALL
    Java_com_sun_java_accessibility_internal_AccessBridge_propertyChildChange(JNIEnv *env, jobject callingObj,
                                                                        jobject event, jobject source,
                                                                        jobject oldValue, jobject newValue) {
        theJavaAccessBridge->firePropertyChildChange(env, callingObj,
                                                        event, source,
                                                        oldValue, newValue);
    }

    JNIEXPORT void JNICALL
    Java_com_sun_java_accessibility_internal_AccessBridge_propertyActiveDescendentChange(JNIEnv *env, jobject callingObj,
                                                                                jobject event,  jobject source,
                                                                                jobject oldValue,
                                                                                jobject newValue) {
        theJavaAccessBridge->firePropertyActiveDescendentChange(env, callingObj,
                                                                event, source,
                                                                oldValue, newValue);
    }

    JNIEXPORT void JNICALL
    Java_com_sun_java_accessibility_internal_AccessBridge_propertyTableModelChange(JNIEnv *env, jobject callingObj,
                                                                            jobject event,  jobject source,
                                                                            jstring oldValue, jstring newValue) {

        theJavaAccessBridge->firePropertyTableModelChange(env, callingObj,
                                                            event, source,
                                                            oldValue, newValue);
    }

#define HANDLE_STANDARD_EVENT_FROM_JAVA(function, method) \
    JNIEXPORT void JNICALL \
    function(JNIEnv *env, jobject callingObj, jobject event, jobject source) { \
        theJavaAccessBridge->method(env, callingObj, event, source); \
    }


    JNIEXPORT void JNICALL
    Java_com_sun_java_accessibility_internal_AccessBridge_javaShutdown(JNIEnv *env, jobject callingObj) {
        theJavaAccessBridge->javaShutdown(env, callingObj);
    }

    HANDLE_STANDARD_EVENT_FROM_JAVA(Java_com_sun_java_accessibility_internal_AccessBridge_focusGained, fireFocusGained)
    HANDLE_STANDARD_EVENT_FROM_JAVA(Java_com_sun_java_accessibility_internal_AccessBridge_focusLost, fireFocusLost)
    HANDLE_STANDARD_EVENT_FROM_JAVA(Java_com_sun_java_accessibility_internal_AccessBridge_caretUpdate, fireCaretUpdate)
    HANDLE_STANDARD_EVENT_FROM_JAVA(Java_com_sun_java_accessibility_internal_AccessBridge_mouseClicked, fireMouseClicked)
    HANDLE_STANDARD_EVENT_FROM_JAVA(Java_com_sun_java_accessibility_internal_AccessBridge_mouseEntered, fireMouseEntered)
    HANDLE_STANDARD_EVENT_FROM_JAVA(Java_com_sun_java_accessibility_internal_AccessBridge_mouseExited, fireMouseExited)
    HANDLE_STANDARD_EVENT_FROM_JAVA(Java_com_sun_java_accessibility_internal_AccessBridge_mousePressed, fireMousePressed)
    HANDLE_STANDARD_EVENT_FROM_JAVA(Java_com_sun_java_accessibility_internal_AccessBridge_mouseReleased, fireMouseReleased)
    HANDLE_STANDARD_EVENT_FROM_JAVA(Java_com_sun_java_accessibility_internal_AccessBridge_menuCanceled, fireMenuCanceled)
    HANDLE_STANDARD_EVENT_FROM_JAVA(Java_com_sun_java_accessibility_internal_AccessBridge_menuDeselected, fireMenuDeselected)
    HANDLE_STANDARD_EVENT_FROM_JAVA(Java_com_sun_java_accessibility_internal_AccessBridge_menuSelected, fireMenuSelected)
    HANDLE_STANDARD_EVENT_FROM_JAVA(Java_com_sun_java_accessibility_internal_AccessBridge_popupMenuCanceled, firePopupMenuCanceled)
    HANDLE_STANDARD_EVENT_FROM_JAVA(Java_com_sun_java_accessibility_internal_AccessBridge_popupMenuWillBecomeInvisible, firePopupMenuWillBecomeInvisible)
    HANDLE_STANDARD_EVENT_FROM_JAVA(Java_com_sun_java_accessibility_internal_AccessBridge_popupMenuWillBecomeVisible, firePopupMenuWillBecomeVisible)

    /*
     * Map a HWND to a Java component
     *
     * Class:     com_sun_java_accessibility_internal_AccessBridge
     * Method:    jawtGetComponentFromNativeWindowHandle
     * Signature: (I)Ljava/awt/Component;
     */
    JNIEXPORT jobject JNICALL
    Java_com_sun_java_accessibility_internal_AccessBridge_jawtGetComponentFromNativeWindowHandle
        (JNIEnv *env, jobject callingObj, jint windowHandle) {

    JAWT awt;
    jboolean result;
    jobject component = (jobject)0;

    // Get the AWT
    awt.version = JAWT_VERSION_1_4;
    result = JAWT_GetAWT(env, &awt);
    if (result == JNI_FALSE) {
        return (jobject)0;
    }

    // Get the component
    return awt.GetComponent(env, (void *)windowHandle);
    }


    /*
     * Map a Java component to a HWND
     *
     * Class:     com_sun_java_accessibility_internal_AccessBridge
     * Method:    jawtGetNativeWindowHandleFromComponent
     * Signature: (Ljava/awt/Component;)I
     */
    JNIEXPORT jint JNICALL
    Java_com_sun_java_accessibility_internal_AccessBridge_jawtGetNativeWindowHandleFromComponent
        (JNIEnv *env, jobject callingObj, jobject component) {

        JAWT awt;
        JAWT_DrawingSurface* ds;
        JAWT_DrawingSurfaceInfo* dsi;
        JAWT_Win32DrawingSurfaceInfo* dsi_win;
        jboolean result;
        // jint lock;
        jint windowHandle = -1;

        // Get the AWT
        awt.version = JAWT_VERSION_1_4;
        result = JAWT_GetAWT(env, &awt);
        if (result == JNI_FALSE) {
            return -1;
        }

        // Get the drawing surface
        ds = awt.GetDrawingSurface(env, component);
        if (ds == NULL) {
            return -1;
        }

        // Get the drawing surface info
        dsi = ds->GetDrawingSurfaceInfo(ds);

        // Get the platform-specific drawing info
        dsi_win = (JAWT_Win32DrawingSurfaceInfo *)dsi->platformInfo;

        // Get the window handle
        windowHandle = (jint)dsi_win->hwnd;

        // Free the drawing surface info
        ds->FreeDrawingSurfaceInfo(dsi);

        // Free the drawing surface
        awt.FreeDrawingSurface(ds);

        return windowHandle;
    }

}
